Skip to main content

synta_codegen/
c_impl_codegen.rs

1//! C implementation code generator for encoder/decoder functions
2
3use crate::ast::*;
4use crate::c_codegen::{generate_c_alphabet_expr, get_c_type};
5use crate::naming::{to_pascal_case, to_snake_case};
6use std::fmt::Write;
7
8/// Controls PATTERN (regex) constraint validation in generated encode functions.
9///
10/// Without a regex library, PATTERN constraints are skipped and a comment is
11/// emitted documenting which flag enables runtime validation.
12#[derive(Debug, Clone, Default, PartialEq, Eq)]
13pub enum PatternMode {
14    /// Emit a skip comment; no runtime validation (default).
15    #[default]
16    Skip,
17    /// Validate using POSIX ERE (`<regex.h>`, `regcomp`/`regexec`).
18    ///
19    /// Requires linking with a POSIX-compatible C library.  The generated
20    /// code allocates a null-terminated copy of each string before calling
21    /// `regexec`.
22    Posix,
23    /// Validate using PCRE2 (`<pcre2.h>`, `pcre2_compile`/`pcre2_match`).
24    ///
25    /// Requires linking against `libpcre2-8`.  PCRE2 supports a superset of
26    /// the POSIX ERE syntax and handles the full ASN.1 PATTERN value notation.
27    Pcre2,
28}
29
30/// Configuration for C implementation generation
31#[derive(Debug, Clone, Default)]
32pub struct CImplConfig {
33    /// Header file to include
34    pub header_file: String,
35    /// Generate _decode_arena() variants
36    pub arena_mode: bool,
37    /// Controls PATTERN constraint validation (default: `PatternMode::Skip`)
38    pub pattern_mode: PatternMode,
39    /// Emit CONTAINING constraint validation using a scratch decoder (default: false).
40    ///
41    /// When true, fields constrained with `CONTAINING InnerType` emit a
42    /// `synta_decoder_new` / `inner_type_decode` / `synta_decoder_free` block
43    /// that verifies the bytes actually hold a valid DER-encoded `InnerType`.
44    pub with_containing: bool,
45}
46
47/// Context threaded through field-encode helpers.
48struct EncodeCtx<'a> {
49    mode: &'a PatternMode,
50    with_containing: bool,
51    defs: &'a [Definition],
52}
53
54/// Generate C implementation file
55pub fn generate_c_impl(
56    module: &Module,
57    config: CImplConfig,
58) -> Result<String, Box<dyn std::error::Error>> {
59    let mut output = String::new();
60
61    // Header comment
62    writeln!(
63        &mut output,
64        "/* Generated implementation from ASN.1 module {} */",
65        module.name
66    )?;
67    writeln!(&mut output, "/* DO NOT EDIT - auto-generated code */")?;
68    writeln!(&mut output)?;
69
70    // Includes
71    writeln!(&mut output, "#include \"{}\"", config.header_file)?;
72    writeln!(&mut output, "#include <string.h>")?;
73    writeln!(&mut output, "#include <stdlib.h>")?;
74    match config.pattern_mode {
75        PatternMode::Posix => {
76            writeln!(&mut output, "#include <regex.h>")?;
77        }
78        PatternMode::Pcre2 => {
79            writeln!(&mut output, "#define PCRE2_CODE_UNIT_WIDTH 8")?;
80            writeln!(&mut output, "#include <pcre2.h>")?;
81        }
82        PatternMode::Skip => {}
83    }
84    writeln!(&mut output)?;
85
86    let mode = config.pattern_mode;
87    let with_containing = config.with_containing;
88
89    // Generate implementations for each type
90    for def in &module.definitions {
91        generate_type_impl(
92            &mut output,
93            def,
94            &mode,
95            with_containing,
96            &module.definitions,
97        )?;
98        writeln!(&mut output)?;
99    }
100
101    // Generate arena implementations if enabled
102    if config.arena_mode {
103        for def in &module.definitions {
104            generate_type_arena_impl(&mut output, def, &module.definitions)?;
105            writeln!(&mut output)?;
106        }
107    }
108
109    Ok(output)
110}
111
112/// Dispatch to the appropriate C implementation emitter for a single top-level
113/// ASN.1 definition.
114///
115/// Handles SEQUENCE, SET, CHOICE, INTEGER with named numbers, ENUMERATED,
116/// SEQUENCE OF, SET OF, constrained SEQUENCE OF / SET OF, and type aliases.
117/// For SEQUENCE / SET types where every field is OPTIONAL or carries a DEFAULT,
118/// also emits a `<prefix>_default` zero-constructor via
119/// [`generate_sequence_default_impl`].
120fn generate_type_impl(
121    output: &mut String,
122    def: &Definition,
123    mode: &PatternMode,
124    with_containing: bool,
125    defs: &[Definition],
126) -> Result<(), Box<dyn std::error::Error>> {
127    match &def.ty {
128        Type::Sequence(fields) => {
129            generate_sequence_impl(
130                output,
131                &def.name,
132                fields,
133                mode,
134                with_containing,
135                defs,
136                false,
137            )?;
138            if fields.iter().all(|f| f.optional || f.default.is_some()) {
139                writeln!(output)?;
140                generate_sequence_default_impl(output, &def.name, fields)?;
141            }
142        }
143        Type::Set(fields) => {
144            generate_sequence_impl(output, &def.name, fields, mode, with_containing, defs, true)?;
145            if fields.iter().all(|f| f.optional || f.default.is_some()) {
146                writeln!(output)?;
147                generate_sequence_default_impl(output, &def.name, fields)?;
148            }
149        }
150        Type::Choice(variants) => {
151            generate_choice_impl(output, &def.name, variants, defs)?;
152        }
153        Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
154            generate_named_integer_impl(output, &def.name)?;
155        }
156        Type::Enumerated(_) => {
157            generate_enum_impl(output, &def.name)?;
158        }
159        Type::SequenceOf(inner, opt_constraint) => {
160            let size_c = opt_constraint.as_ref().map(legacy_size_to_bounds);
161            generate_array_impl(output, &def.name, inner, true, size_c, defs)?;
162        }
163        Type::SetOf(inner, opt_constraint) => {
164            let size_c = opt_constraint.as_ref().map(legacy_size_to_bounds);
165            generate_array_impl(output, &def.name, inner, false, size_c, defs)?;
166        }
167        // Constrained SEQUENCE OF / SET OF: the outer constraint holds the SIZE spec.
168        Type::Constrained {
169            base_type,
170            constraint,
171        } => {
172            let size_c = modern_size_to_bounds(&constraint.spec);
173            match base_type.as_ref() {
174                Type::SequenceOf(inner, _) => {
175                    generate_array_impl(output, &def.name, inner, true, size_c, defs)?;
176                }
177                Type::SetOf(inner, _) => {
178                    generate_array_impl(output, &def.name, inner, false, size_c, defs)?;
179                }
180                _ => {
181                    generate_simple_impl(output, &def.name, &def.ty)?;
182                }
183            }
184        }
185        _ => {
186            // Simple type alias - generate wrapper functions for the base type
187            generate_simple_impl(output, &def.name, &def.ty)?;
188        }
189    }
190    Ok(())
191}
192
193/// Convert a legacy `SizeConstraint` to `(min, max)` element-count bounds.
194fn legacy_size_to_bounds(sc: &SizeConstraint) -> ArraySizeBounds {
195    match sc {
196        SizeConstraint::Fixed(n) => (Some(*n), Some(*n)),
197        SizeConstraint::Range(min, max) => (*min, *max),
198    }
199}
200
201/// Extract `(min, max)` element-count bounds from a modern `ConstraintSpec`
202/// if it is a `SizeConstraint` wrapping a `ValueRange` or `SingleValue`.
203/// Returns `None` for all other constraint forms.
204fn modern_size_to_bounds(spec: &ConstraintSpec) -> Option<ArraySizeBounds> {
205    if let ConstraintSpec::Subtype(SubtypeConstraint::SizeConstraint(inner)) = spec {
206        match inner.as_ref() {
207            SubtypeConstraint::ValueRange { min, max } => {
208                let lo = match min {
209                    ConstraintValue::Integer(n) if *n >= 0 => Some(*n as u64),
210                    _ => None,
211                };
212                let hi = match max {
213                    ConstraintValue::Integer(n) if *n >= 0 => Some(*n as u64),
214                    _ => None,
215                };
216                Some((lo, hi))
217            }
218            SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) if *n >= 0 => {
219                Some((Some(*n as u64), Some(*n as u64)))
220            }
221            _ => None,
222        }
223    } else {
224        None
225    }
226}
227
228/// Generate encode/decode/free C functions for a top-level SEQUENCE or SET type.
229///
230/// **Memory contract for the generated decoder** — if decoding fails partway
231/// through, any fields that were already successfully decoded and heap-allocated
232/// (e.g. `SyntaInteger*`, `SyntaOctetString*`) are **not** freed before
233/// returning the error.  Callers are responsible for calling `<name>_free` on
234/// the partially-filled output struct to avoid leaks.
235fn generate_sequence_impl(
236    output: &mut String,
237    name: &str,
238    fields: &[SequenceField],
239    mode: &PatternMode,
240    with_containing: bool,
241    defs: &[Definition],
242    is_set: bool,
243) -> Result<(), Box<dyn std::error::Error>> {
244    let struct_name = to_pascal_case(name);
245    let fn_prefix = to_snake_case(name);
246    let container_kind = if is_set { "set" } else { "sequence" };
247    let enter_fn = if is_set {
248        "synta_decoder_enter_set"
249    } else {
250        "synta_decoder_enter_sequence"
251    };
252    let start_fn = if is_set {
253        "synta_encoder_start_set"
254    } else {
255        "synta_encoder_start_sequence"
256    };
257
258    // Decode function
259    writeln!(
260        output,
261        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out) {{",
262        fn_prefix, struct_name
263    )?;
264    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
265    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
266    writeln!(output, "    }}")?;
267    writeln!(output)?;
268    writeln!(output, "    // Enter {}", container_kind.to_uppercase())?;
269    writeln!(output, "    SyntaDecoder* seq_decoder = NULL;")?;
270    writeln!(
271        output,
272        "    SyntaErrorCode err = {}(decoder, &seq_decoder);",
273        enter_fn
274    )?;
275    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
276    writeln!(output, "        return err;")?;
277    writeln!(output, "    }}")?;
278    writeln!(output)?;
279
280    // Decode each field
281    for field in fields {
282        let field_name = to_snake_case(&field.name);
283
284        if field.optional {
285            writeln!(output, "    // Decode optional field: {}", field_name)?;
286            let tag_check = get_tag_check_condition(&field.ty, defs);
287            writeln!(output, "    {{")?;
288            writeln!(output, "        SyntaTag tag;")?;
289            writeln!(
290                output,
291                "        if (synta_decoder_peek_tag(seq_decoder, &tag) == SyntaErrorCode_Success &&"
292            )?;
293            writeln!(output, "            ({})) {{", tag_check)?;
294            generate_field_decode(output, &field.ty, &format!("out->{}", field_name), 3, defs)?;
295            writeln!(output, "            out->has_{} = true;", field_name)?;
296            writeln!(output, "        }} else {{")?;
297            writeln!(output, "            out->has_{} = false;", field_name)?;
298            writeln!(output, "        }}")?;
299            writeln!(output, "    }}")?;
300        } else {
301            writeln!(output, "    // Decode field: {}", field_name)?;
302            generate_field_decode(output, &field.ty, &format!("out->{}", field_name), 1, defs)?;
303        }
304        writeln!(output)?;
305    }
306
307    writeln!(output, "    synta_decoder_free(seq_decoder);")?;
308    writeln!(output, "    return SyntaErrorCode_Success;")?;
309    writeln!(output, "}}")?;
310    writeln!(output)?;
311
312    // Encode function
313    writeln!(
314        output,
315        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value) {{",
316        fn_prefix, struct_name
317    )?;
318    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
319    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
320    writeln!(output, "    }}")?;
321    writeln!(output)?;
322    writeln!(output, "    // Start {}", container_kind.to_uppercase())?;
323    writeln!(output, "    SyntaEncoder* seq_encoder = NULL;")?;
324    writeln!(
325        output,
326        "    SyntaErrorCode err = {}(encoder, &seq_encoder);",
327        start_fn
328    )?;
329    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
330    writeln!(output)?;
331
332    let enc_ctx = EncodeCtx {
333        mode,
334        with_containing,
335        defs,
336    };
337    for field in fields {
338        let field_name = to_snake_case(&field.name);
339
340        if field.optional {
341            writeln!(output, "    if (value->has_{}) {{", field_name)?;
342            generate_field_encode(
343                output,
344                &field.ty,
345                &format!("value->{}", field_name),
346                "seq_encoder",
347                2,
348                &enc_ctx,
349            )?;
350            writeln!(output, "    }}")?;
351        } else {
352            generate_field_encode(
353                output,
354                &field.ty,
355                &format!("value->{}", field_name),
356                "seq_encoder",
357                1,
358                &enc_ctx,
359            )?;
360        }
361    }
362
363    writeln!(output)?;
364    writeln!(
365        output,
366        "    err = synta_encoder_end_constructed(seq_encoder);"
367    )?;
368    writeln!(output, "    return err;")?;
369    writeln!(output, "}}")?;
370    writeln!(output)?;
371
372    // Free function
373    writeln!(output, "void {}_free({}* value) {{", fn_prefix, struct_name)?;
374    writeln!(output, "    if (value == NULL) return;")?;
375    writeln!(output)?;
376
377    for field in fields {
378        let field_name = to_snake_case(&field.name);
379        match &field.ty {
380            Type::Integer(_, _) => {
381                if field.optional {
382                    writeln!(output, "    if (value->has_{}) {{", field_name)?;
383                    writeln!(output, "        synta_integer_free(value->{});", field_name)?;
384                    writeln!(output, "    }}")?;
385                } else {
386                    writeln!(output, "    synta_integer_free(value->{});", field_name)?;
387                }
388            }
389            Type::ObjectIdentifier => {
390                if field.optional {
391                    writeln!(output, "    if (value->has_{}) {{", field_name)?;
392                    writeln!(output, "        synta_oid_free(value->{});", field_name)?;
393                    writeln!(output, "    }}")?;
394                } else {
395                    writeln!(output, "    synta_oid_free(value->{});", field_name)?;
396                }
397            }
398            Type::OctetString(_)
399            | Type::Utf8String(_)
400            | Type::PrintableString(_)
401            | Type::IA5String(_)
402            | Type::UtcTime
403            | Type::GeneralizedTime
404            | Type::Any
405            | Type::AnyDefinedBy(_) => {
406                if field.optional {
407                    writeln!(output, "    if (value->has_{}) {{", field_name)?;
408                    writeln!(
409                        output,
410                        "        synta_octet_string_free(value->{});",
411                        field_name
412                    )?;
413                    writeln!(output, "    }}")?;
414                } else {
415                    writeln!(
416                        output,
417                        "    synta_octet_string_free(value->{});",
418                        field_name
419                    )?;
420                }
421            }
422            Type::BitString(_) => {
423                if field.optional {
424                    writeln!(output, "    if (value->has_{}) {{", field_name)?;
425                    writeln!(
426                        output,
427                        "        synta_byte_array_free(&value->{}.data);",
428                        field_name
429                    )?;
430                    writeln!(output, "    }}")?;
431                } else {
432                    writeln!(
433                        output,
434                        "    synta_byte_array_free(&value->{}.data);",
435                        field_name
436                    )?;
437                }
438            }
439            Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
440                // Free array elements if needed
441                if needs_freeing(inner, defs) {
442                    writeln!(output, "    // Free array elements")?;
443                    writeln!(
444                        output,
445                        "    for (size_t i = 0; i < value->{}_count; i++) {{",
446                        field_name
447                    )?;
448                    generate_free_element(
449                        output,
450                        inner,
451                        &format!("value->{}[i]", field_name),
452                        defs,
453                    )?;
454                    writeln!(output, "    }}")?;
455                }
456                writeln!(output, "    // Free array")?;
457                writeln!(output, "    free(value->{});", field_name)?;
458            }
459            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
460                // Free nested inline SEQUENCE/SET fields
461                for inner_field in inner_fields {
462                    if matches!(inner_field.ty, Type::Null) {
463                        continue;
464                    }
465
466                    let inner_name = to_snake_case(&inner_field.name);
467                    let nested_var = format!("value->{}.{}", field_name, inner_name);
468
469                    match &inner_field.ty {
470                        Type::Integer(_, _) => {
471                            if inner_field.optional {
472                                writeln!(
473                                    output,
474                                    "    if (value->{}.has_{}) {{",
475                                    field_name, inner_name
476                                )?;
477                                writeln!(output, "        synta_integer_free({});", nested_var)?;
478                                writeln!(output, "    }}")?;
479                            } else {
480                                writeln!(output, "    synta_integer_free({});", nested_var)?;
481                            }
482                        }
483                        Type::ObjectIdentifier => {
484                            if inner_field.optional {
485                                writeln!(
486                                    output,
487                                    "    if (value->{}.has_{}) {{",
488                                    field_name, inner_name
489                                )?;
490                                writeln!(output, "        synta_oid_free({});", nested_var)?;
491                                writeln!(output, "    }}")?;
492                            } else {
493                                writeln!(output, "    synta_oid_free({});", nested_var)?;
494                            }
495                        }
496                        Type::OctetString(_)
497                        | Type::Utf8String(_)
498                        | Type::PrintableString(_)
499                        | Type::IA5String(_) => {
500                            if inner_field.optional {
501                                writeln!(
502                                    output,
503                                    "    if (value->{}.has_{}) {{",
504                                    field_name, inner_name
505                                )?;
506                                writeln!(
507                                    output,
508                                    "        synta_octet_string_free({});",
509                                    nested_var
510                                )?;
511                                writeln!(output, "    }}")?;
512                            } else {
513                                writeln!(output, "    synta_octet_string_free({});", nested_var)?;
514                            }
515                        }
516                        Type::BitString(_) => {
517                            if inner_field.optional {
518                                writeln!(
519                                    output,
520                                    "    if (value->{}.has_{}) {{",
521                                    field_name, inner_name
522                                )?;
523                                writeln!(
524                                    output,
525                                    "        synta_byte_array_free(&{}.data);",
526                                    nested_var
527                                )?;
528                                writeln!(output, "    }}")?;
529                            } else {
530                                writeln!(
531                                    output,
532                                    "    synta_byte_array_free(&{}.data);",
533                                    nested_var
534                                )?;
535                            }
536                        }
537                        Type::TypeRef(type_name) => {
538                            let name = type_name.clone();
539                            if inner_field.optional {
540                                writeln!(
541                                    output,
542                                    "    if (value->{}.has_{}) {{",
543                                    field_name, inner_name
544                                )?;
545                                emit_typeref_free(output, &name, &nested_var, "        ", defs)?;
546                                writeln!(output, "    }}")?;
547                            } else {
548                                emit_typeref_free(output, &name, &nested_var, "    ", defs)?;
549                            }
550                        }
551                        _ => {}
552                    }
553                }
554            }
555            Type::TypeRef(type_name) => {
556                // Only emit anything if the resolved type actually needs heap cleanup
557                if needs_freeing(&field.ty, defs) {
558                    let name = type_name.clone();
559                    if field.optional {
560                        writeln!(output, "    if (value->has_{}) {{", field_name)?;
561                        emit_typeref_free(
562                            output,
563                            &name,
564                            &format!("value->{}", field_name),
565                            "        ",
566                            defs,
567                        )?;
568                        writeln!(output, "    }}")?;
569                    } else {
570                        emit_typeref_free(
571                            output,
572                            &name,
573                            &format!("value->{}", field_name),
574                            "    ",
575                            defs,
576                        )?;
577                    }
578                }
579            }
580            Type::Tagged { inner, .. }
581            | Type::Constrained {
582                base_type: inner, ..
583            } => {
584                // Unwrap tag/constraint wrapper and free the underlying value
585                let base_ty = unwrap_type(inner);
586                if field.optional {
587                    writeln!(output, "    if (value->has_{}) {{", field_name)?;
588                    generate_free_stmt(
589                        output,
590                        base_ty,
591                        &format!("value->{}", field_name),
592                        "        ",
593                    )?;
594                    writeln!(output, "    }}")?;
595                } else {
596                    generate_free_stmt(output, base_ty, &format!("value->{}", field_name), "    ")?;
597                }
598            }
599            _ => {}
600        }
601    }
602
603    writeln!(output, "}}")?;
604
605    Ok(())
606}
607
608/// Generate a `TypeName_default(void)` constructor for sequences where every
609/// field is either OPTIONAL or carries a DEFAULT value.
610///
611/// The returned struct is zero-initialised (`{0}`), so optional `has_*` flags
612/// are already `false` and all pointers are `NULL`.  Fields with a DEFAULT are
613/// annotated:
614///
615/// * `BOOLEAN DEFAULT TRUE` — assigned directly (`out.field = true;`).
616/// * `REAL DEFAULT <n>` — assigned directly (`out.field = <n>;`).
617/// * All other types — a comment is emitted instead, because the C
618///   representation is a pointer that cannot be trivially assigned a literal.
619fn generate_sequence_default_impl(
620    output: &mut String,
621    name: &str,
622    fields: &[SequenceField],
623) -> Result<(), Box<dyn std::error::Error>> {
624    let c_name = to_pascal_case(name);
625    let fn_prefix = to_snake_case(name);
626
627    writeln!(output, "{c_name} {fn_prefix}_default(void) {{")?;
628    writeln!(output, "    {c_name} out = {{0}};")?;
629
630    for field in fields {
631        let Some(ref default_val) = field.default else {
632            continue;
633        };
634        let field_name = to_snake_case(&field.name);
635        match unwrap_type(&field.ty) {
636            Type::Boolean => {
637                // Only emit an assignment when the default is not false (zero already gives false).
638                let is_true = default_val.eq_ignore_ascii_case("true");
639                if is_true {
640                    writeln!(output, "    out.{field_name} = true;")?;
641                }
642            }
643            Type::Real => {
644                writeln!(output, "    out.{field_name} = {default_val};")?;
645            }
646            _ => {
647                writeln!(
648                    output,
649                    "    /* out.{field_name}: DEFAULT {default_val} — set by caller or decoder */"
650                )?;
651            }
652        }
653    }
654
655    writeln!(output, "    return out;")?;
656    writeln!(output, "}}")?;
657    Ok(())
658}
659
660/// Generate encode, decode, and free C functions for a top-level CHOICE type.
661///
662/// The decoder peeks at the next tag, walks an if-else chain to select the
663/// matching variant, sets the discriminant field, and delegates to the
664/// variant's type-specific decoder.  The encoder switches on the discriminant
665/// and calls the matching variant encoder.  The free function switches on the
666/// discriminant and frees only the active variant's heap data.
667fn generate_choice_impl(
668    output: &mut String,
669    name: &str,
670    variants: &[ChoiceVariant],
671    defs: &[Definition],
672) -> Result<(), Box<dyn std::error::Error>> {
673    let struct_name = to_pascal_case(name);
674    let fn_prefix = to_snake_case(name);
675    let tag_enum_name = format!("{}Tag", struct_name);
676
677    // Decode function
678    writeln!(
679        output,
680        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out) {{",
681        fn_prefix, struct_name
682    )?;
683    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
684    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
685    writeln!(output, "    }}")?;
686    writeln!(output)?;
687    writeln!(
688        output,
689        "    // Peek at the next tag to determine which variant"
690    )?;
691    writeln!(output, "    SyntaTag tag;")?;
692    writeln!(
693        output,
694        "    SyntaErrorCode err = synta_decoder_peek_tag(decoder, &tag);"
695    )?;
696    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
697    writeln!(output, "        return err;")?;
698    writeln!(output, "    }}")?;
699    writeln!(output)?;
700
701    // Generate if-else chain to match variants by tag
702    for (i, variant) in variants.iter().enumerate() {
703        let variant_name = to_snake_case(&variant.name);
704        let variant_pascal = to_pascal_case(&variant.name);
705        let tag_check = get_tag_check_condition(&variant.ty, defs);
706
707        let else_if = if i == 0 { "if" } else { "} else if" };
708
709        writeln!(output, "    {} ({}) {{", else_if, tag_check)?;
710        writeln!(output, "        // Decode {} variant", variant_name)?;
711        writeln!(
712            output,
713            "        out->tag = {}_{};",
714            tag_enum_name, variant_pascal
715        )?;
716        generate_choice_variant_decode(
717            output,
718            &variant.ty,
719            &format!("out->value.{}", variant_name),
720        )?;
721        writeln!(output, "        return SyntaErrorCode_Success;")?;
722    }
723
724    writeln!(output, "    }} else {{")?;
725    writeln!(
726        output,
727        "        /* no matching variant found for the observed tag */"
728    )?;
729    writeln!(output, "        return SyntaErrorCode_InvalidEncoding;")?;
730    writeln!(output, "    }}")?;
731    writeln!(output, "}}")?;
732    writeln!(output)?;
733
734    // Encode function
735    writeln!(
736        output,
737        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value) {{",
738        fn_prefix, struct_name
739    )?;
740    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
741    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
742    writeln!(output, "    }}")?;
743    writeln!(output)?;
744    writeln!(output, "    SyntaErrorCode err;")?;
745    writeln!(output, "    switch (value->tag) {{")?;
746
747    // Generate case for each variant
748    for variant in variants {
749        let variant_name = to_snake_case(&variant.name);
750        let variant_pascal = to_pascal_case(&variant.name);
751
752        writeln!(
753            output,
754            "        case {}_{}: {{",
755            tag_enum_name, variant_pascal
756        )?;
757        generate_choice_variant_encode(
758            output,
759            &variant.ty,
760            &format!("value->value.{}", variant_name),
761        )?;
762        writeln!(output, "            return SyntaErrorCode_Success;")?;
763        writeln!(output, "        }}")?;
764    }
765
766    writeln!(output, "        default:")?;
767    writeln!(output, "            return SyntaErrorCode_InvalidEncoding;")?;
768    writeln!(output, "    }}")?;
769    writeln!(output, "}}")?;
770    writeln!(output)?;
771
772    // Free function
773    writeln!(output, "void {}_free({}* value) {{", fn_prefix, struct_name)?;
774    writeln!(output, "    if (value == NULL) return;")?;
775    writeln!(output)?;
776    writeln!(output, "    switch (value->tag) {{")?;
777
778    // Generate case for each variant that needs freeing
779    for variant in variants {
780        let variant_name = to_snake_case(&variant.name);
781        let variant_pascal = to_pascal_case(&variant.name);
782
783        // Check if this variant needs freeing
784        if needs_freeing(&variant.ty, defs) {
785            writeln!(
786                output,
787                "        case {}_{}: {{",
788                tag_enum_name, variant_pascal
789            )?;
790            generate_choice_variant_free(
791                output,
792                &variant.ty,
793                &format!("value->value.{}", variant_name),
794                defs,
795            )?;
796            writeln!(output, "            break;")?;
797            writeln!(output, "        }}")?;
798        }
799    }
800
801    writeln!(output, "        default:")?;
802    writeln!(output, "            break;")?;
803    writeln!(output, "    }}")?;
804    writeln!(output, "}}")?;
805
806    Ok(())
807}
808
809/// (min, max) element-count bounds for a SEQUENCE OF / SET OF SIZE constraint.
810/// `None` means the bound is absent (unbounded in that direction).
811type ArraySizeBounds = (Option<u64>, Option<u64>);
812
813/// Generate encode, decode, and free C functions for a top-level SEQUENCE OF
814/// or SET OF type.
815///
816/// The decoder uses a heap array with an initial capacity of 4 elements,
817/// doubling on overflow (`realloc`).  If any element decode fails, the
818/// partial array and the inner `array_decoder` are freed before returning the
819/// error.  An optional SIZE constraint is checked after all elements are
820/// decoded (decode path) or before encoding begins (encode path).  The free
821/// function iterates over the array, frees each element that requires it,
822/// then frees the array pointer itself.
823fn generate_array_impl(
824    output: &mut String,
825    name: &str,
826    inner_ty: &Type,
827    is_sequence: bool,
828    size_constraint: Option<ArraySizeBounds>,
829    defs: &[Definition],
830) -> Result<(), Box<dyn std::error::Error>> {
831    let struct_name = to_pascal_case(name);
832    let fn_prefix = to_snake_case(name);
833    let container_type = if is_sequence { "sequence" } else { "set" };
834    let elem_c_type = get_c_type(inner_ty);
835    let array_type = format!("{}*", elem_c_type);
836
837    // Decode function
838    writeln!(
839        output,
840        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, struct {}* out) {{",
841        fn_prefix, struct_name
842    )?;
843    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
844    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
845    writeln!(output, "    }}")?;
846    writeln!(output)?;
847
848    writeln!(output, "    // Enter SEQUENCE/SET")?;
849    writeln!(output, "    SyntaDecoder* array_decoder = NULL;")?;
850    writeln!(
851        output,
852        "    SyntaErrorCode err = synta_decoder_enter_{}(decoder, &array_decoder);",
853        container_type
854    )?;
855    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
856    writeln!(output, "        return err;")?;
857    writeln!(output, "    }}")?;
858    writeln!(output)?;
859
860    writeln!(output, "    // Allocate dynamic array for elements")?;
861    writeln!(output, "    size_t capacity = 4; // Initial capacity")?;
862    writeln!(output, "    size_t count = 0;")?;
863    writeln!(
864        output,
865        "    {} array = ({})calloc(capacity, sizeof({}));",
866        array_type, array_type, elem_c_type
867    )?;
868    writeln!(output, "    if (array == NULL) {{")?;
869    writeln!(output, "        synta_decoder_free(array_decoder);")?;
870    writeln!(output, "        return SyntaErrorCode_OutOfMemory;")?;
871    writeln!(output, "    }}")?;
872    writeln!(output)?;
873
874    writeln!(
875        output,
876        "    while (!synta_decoder_at_end(array_decoder)) {{"
877    )?;
878    writeln!(output, "        // Grow array if needed")?;
879    writeln!(output, "        if (count >= capacity) {{")?;
880    writeln!(output, "            capacity *= 2;")?;
881    writeln!(
882        output,
883        "            {} new_array = ({})realloc(array, capacity * sizeof({}));",
884        array_type, array_type, elem_c_type
885    )?;
886    writeln!(output, "            if (new_array == NULL) {{")?;
887    writeln!(output, "                free(array);")?;
888    writeln!(output, "                synta_decoder_free(array_decoder);")?;
889    writeln!(output, "                return SyntaErrorCode_OutOfMemory;")?;
890    writeln!(output, "            }}")?;
891    writeln!(output, "            array = new_array;")?;
892    writeln!(output, "        }}")?;
893    writeln!(output)?;
894
895    // Generate decode for element
896    writeln!(output, "        // Decode element")?;
897    generate_decode_element_toplevel(output, inner_ty, "array_decoder", "array[count]")?;
898    writeln!(output, "        count++;")?;
899    writeln!(output, "    }}")?;
900    writeln!(output)?;
901
902    // SIZE constraint check after the decode loop
903    if let Some(size_spec) = size_constraint {
904        emit_array_size_check(output, size_spec, "count")?;
905    }
906
907    writeln!(output, "    out->count = count;")?;
908    writeln!(output, "    out->items = array;")?;
909    writeln!(output, "    synta_decoder_free(array_decoder);")?;
910    writeln!(output, "    return SyntaErrorCode_Success;")?;
911    writeln!(output, "}}")?;
912    writeln!(output)?;
913
914    // Encode function
915    writeln!(
916        output,
917        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const struct {}* value) {{",
918        fn_prefix, struct_name
919    )?;
920    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
921    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
922    writeln!(output, "    }}")?;
923    writeln!(output)?;
924
925    // SIZE constraint check before encoding
926    if let Some(size_spec) = size_constraint {
927        emit_array_size_check_encode(output, size_spec, "value->count")?;
928    }
929
930    writeln!(output, "    // Start SEQUENCE/SET")?;
931    writeln!(output, "    SyntaEncoder* array_encoder = NULL;")?;
932    writeln!(
933        output,
934        "    SyntaErrorCode err = synta_encoder_start_{}(encoder, &array_encoder);",
935        container_type
936    )?;
937    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
938    writeln!(output)?;
939
940    writeln!(output, "    // Encode each element")?;
941    writeln!(output, "    for (size_t i = 0; i < value->count; i++) {{")?;
942    generate_encode_element_toplevel(output, inner_ty, "array_encoder", "value->items[i]")?;
943    writeln!(output, "    }}")?;
944    writeln!(output)?;
945
946    writeln!(
947        output,
948        "    return synta_encoder_end_constructed(array_encoder);"
949    )?;
950    writeln!(output, "}}")?;
951    writeln!(output)?;
952
953    // Free function
954    writeln!(output, "void {}_free({}* value) {{", fn_prefix, struct_name)?;
955    writeln!(output, "    if (value == NULL) return;")?;
956    writeln!(output)?;
957
958    if needs_freeing(inner_ty, defs) {
959        writeln!(output, "    // Free each element")?;
960        writeln!(output, "    for (size_t i = 0; i < value->count; i++) {{")?;
961        generate_free_element(output, inner_ty, "value->items[i]", defs)?;
962        writeln!(output, "    }}")?;
963        writeln!(output)?;
964    }
965
966    writeln!(output, "    // Free array")?;
967    writeln!(output, "    free(value->items);")?;
968    writeln!(output, "}}")?;
969
970    Ok(())
971}
972
973/// Generate encode and decode C functions for an ASN.1 ENUMERATED type.
974///
975/// The decoder reads a DER INTEGER TLV via `synta_integer_decode`, extracts
976/// the value with `synta_integer_to_i64`, and casts it to the C `enum` type.
977/// The encoder wraps the value in a temporary `SyntaInteger*` and encodes it.
978/// No range validation is performed; the caller is responsible for ensuring
979/// only valid enumerators are stored.
980fn generate_enum_impl(output: &mut String, name: &str) -> Result<(), Box<dyn std::error::Error>> {
981    let fn_prefix = to_snake_case(name);
982
983    // Decode function
984    writeln!(
985        output,
986        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, enum {}* out) {{",
987        fn_prefix,
988        to_pascal_case(name)
989    )?;
990    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
991    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
992    writeln!(output, "    }}")?;
993    writeln!(output)?;
994    writeln!(output, "    SyntaInteger* int_val = NULL;")?;
995    writeln!(
996        output,
997        "    SyntaErrorCode err = synta_decode_integer(decoder, &int_val);"
998    )?;
999    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
1000    writeln!(output)?;
1001    writeln!(output, "    int64_t val;")?;
1002    writeln!(output, "    err = synta_integer_to_i64(int_val, &val);")?;
1003    writeln!(output, "    synta_integer_free(int_val);")?;
1004    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
1005    writeln!(output)?;
1006    writeln!(output, "    *out = (enum {})val;", to_pascal_case(name))?;
1007    writeln!(output, "    return SyntaErrorCode_Success;")?;
1008    writeln!(output, "}}")?;
1009    writeln!(output)?;
1010
1011    // Encode function
1012    writeln!(
1013        output,
1014        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const enum {}* value) {{",
1015        fn_prefix,
1016        to_pascal_case(name)
1017    )?;
1018    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
1019    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
1020    writeln!(output, "    }}")?;
1021    writeln!(output)?;
1022    writeln!(
1023        output,
1024        "    SyntaInteger* int_val = synta_integer_new_i64((int64_t)*value);"
1025    )?;
1026    writeln!(output, "    if (int_val == NULL) {{")?;
1027    writeln!(output, "        return SyntaErrorCode_OutOfMemory;")?;
1028    writeln!(output, "    }}")?;
1029    writeln!(output)?;
1030    writeln!(
1031        output,
1032        "    SyntaErrorCode err = synta_encode_integer(encoder, int_val);"
1033    )?;
1034    writeln!(output, "    synta_integer_free(int_val);")?;
1035    writeln!(output, "    return err;")?;
1036    writeln!(output, "}}")?;
1037
1038    Ok(())
1039}
1040
1041/// Generate decode/encode functions for an unconstrained INTEGER with named numbers.
1042///
1043/// The C type is `typedef int64_t TypeName;` (see the "Why `int64_t`?" note in
1044/// `generate_defines_for_integer` in `c_codegen.rs`), so the pointer parameter
1045/// is `TypeName*` (i.e. `int64_t*`).  No enum cast is performed: the decode
1046/// path assigns `*out = val` directly and the encode path passes `*value`
1047/// straight to `synta_integer_new_i64`, avoiding a round-trip through an enum
1048/// type whose C representation size is implementation-defined.
1049fn generate_named_integer_impl(
1050    output: &mut String,
1051    name: &str,
1052) -> Result<(), Box<dyn std::error::Error>> {
1053    let fn_prefix = to_snake_case(name);
1054    let c_name = to_pascal_case(name);
1055
1056    // Decode function
1057    writeln!(
1058        output,
1059        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out) {{",
1060        fn_prefix, c_name
1061    )?;
1062    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
1063    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
1064    writeln!(output, "    }}")?;
1065    writeln!(output)?;
1066    writeln!(output, "    SyntaInteger* int_val = NULL;")?;
1067    writeln!(
1068        output,
1069        "    SyntaErrorCode err = synta_decode_integer(decoder, &int_val);"
1070    )?;
1071    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
1072    writeln!(output)?;
1073    writeln!(output, "    int64_t val;")?;
1074    writeln!(output, "    err = synta_integer_to_i64(int_val, &val);")?;
1075    writeln!(output, "    synta_integer_free(int_val);")?;
1076    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
1077    writeln!(output)?;
1078    writeln!(output, "    *out = val;")?;
1079    writeln!(output, "    return SyntaErrorCode_Success;")?;
1080    writeln!(output, "}}")?;
1081    writeln!(output)?;
1082
1083    // Encode function
1084    writeln!(
1085        output,
1086        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value) {{",
1087        fn_prefix, c_name
1088    )?;
1089    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
1090    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
1091    writeln!(output, "    }}")?;
1092    writeln!(output)?;
1093    writeln!(
1094        output,
1095        "    SyntaInteger* int_val = synta_integer_new_i64(*value);"
1096    )?;
1097    writeln!(output, "    if (int_val == NULL) {{")?;
1098    writeln!(output, "        return SyntaErrorCode_OutOfMemory;")?;
1099    writeln!(output, "    }}")?;
1100    writeln!(output)?;
1101    writeln!(
1102        output,
1103        "    SyntaErrorCode err = synta_encode_integer(encoder, int_val);"
1104    )?;
1105    writeln!(output, "    synta_integer_free(int_val);")?;
1106    writeln!(output, "    return err;")?;
1107    writeln!(output, "}}")?;
1108
1109    Ok(())
1110}
1111
1112/// Unwrap Tagged/Constrained wrappers to find the underlying base type.
1113fn unwrap_type(ty: &Type) -> &Type {
1114    match ty {
1115        Type::Tagged { inner, .. } => unwrap_type(inner),
1116        Type::Constrained { base_type, .. } => unwrap_type(base_type),
1117        other => other,
1118    }
1119}
1120
1121/// Generate decode/encode wrapper functions for a simple type alias.
1122///
1123/// Dispatches to the correct C API based on the actual underlying type so
1124/// that, e.g., a `MyBool ::= BOOLEAN` alias calls `synta_decode_boolean`
1125/// rather than the formerly hard-coded `synta_decode_integer`.
1126fn generate_simple_impl(
1127    output: &mut String,
1128    name: &str,
1129    ty: &Type,
1130) -> Result<(), Box<dyn std::error::Error>> {
1131    let fn_prefix = to_snake_case(name);
1132    let type_name = to_pascal_case(name);
1133    let base = unwrap_type(ty);
1134
1135    // ── decode ────────────────────────────────────────────────────────────
1136    writeln!(
1137        output,
1138        "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out) {{",
1139        fn_prefix, type_name
1140    )?;
1141    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
1142    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
1143    writeln!(output, "    }}")?;
1144    match base {
1145        Type::Boolean => {
1146            writeln!(output, "    return synta_decode_boolean(decoder, out);")?;
1147        }
1148        Type::Integer(_, _) | Type::Enumerated(_) => {
1149            writeln!(output, "    return synta_decode_integer(decoder, out);")?;
1150        }
1151        Type::Real => {
1152            writeln!(output, "    return synta_decode_real(decoder, out);")?;
1153        }
1154        Type::Null => {
1155            writeln!(output, "    (void)out;")?;
1156            writeln!(output, "    return synta_decode_null(decoder);")?;
1157        }
1158        Type::ObjectIdentifier => {
1159            writeln!(
1160                output,
1161                "    return synta_decode_object_identifier(decoder, out);"
1162            )?;
1163        }
1164        Type::BitString(_) => {
1165            writeln!(
1166                output,
1167                "    return synta_decode_bit_string(decoder, &out->data, &out->unused_bits);"
1168            )?;
1169        }
1170        Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_) => {
1171            writeln!(
1172                output,
1173                "    return synta_decode_octet_string(decoder, out);"
1174            )?;
1175        }
1176        Type::Utf8String(_) => {
1177            writeln!(
1178                output,
1179                "    return synta_decode_utf8_string_os(decoder, out);"
1180            )?;
1181        }
1182        Type::PrintableString(_) => {
1183            writeln!(
1184                output,
1185                "    return synta_decode_printable_string_os(decoder, out);"
1186            )?;
1187        }
1188        Type::IA5String(_) => {
1189            writeln!(
1190                output,
1191                "    return synta_decode_ia5_string_os(decoder, out);"
1192            )?;
1193        }
1194        Type::UtcTime => {
1195            writeln!(output, "    return synta_decode_utctime_os(decoder, out);")?;
1196        }
1197        Type::GeneralizedTime => {
1198            writeln!(
1199                output,
1200                "    return synta_decode_generalized_time_os(decoder, out);"
1201            )?;
1202        }
1203        Type::TypeRef(ref_name) => {
1204            let ref_fn = to_snake_case(ref_name);
1205            writeln!(output, "    return {}_decode(decoder, out);", ref_fn)?;
1206        }
1207        _ => {
1208            writeln!(output, "    /* unsupported simple type alias */")?;
1209            writeln!(output, "    return SyntaErrorCode_InvalidEncoding;")?;
1210        }
1211    }
1212    writeln!(output, "}}")?;
1213    writeln!(output)?;
1214
1215    // ── encode ────────────────────────────────────────────────────────────
1216    writeln!(
1217        output,
1218        "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value) {{",
1219        fn_prefix, type_name
1220    )?;
1221    writeln!(output, "    if (encoder == NULL || value == NULL) {{")?;
1222    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
1223    writeln!(output, "    }}")?;
1224    match base {
1225        Type::Boolean => {
1226            writeln!(output, "    return synta_encode_boolean(encoder, *value);")?;
1227        }
1228        Type::Integer(_, _) | Type::Enumerated(_) => {
1229            writeln!(output, "    return synta_encode_integer(encoder, value);")?;
1230        }
1231        Type::Real => {
1232            writeln!(output, "    return synta_encode_real(encoder, *value);")?;
1233        }
1234        Type::Null => {
1235            writeln!(output, "    (void)value;")?;
1236            writeln!(output, "    return synta_encode_null(encoder);")?;
1237        }
1238        Type::ObjectIdentifier => {
1239            writeln!(
1240                output,
1241                "    return synta_encode_object_identifier(encoder, value);"
1242            )?;
1243        }
1244        Type::BitString(_) => {
1245            writeln!(
1246                output,
1247                "    return synta_encode_bit_string(encoder, value->data.data, value->data.len, value->unused_bits);"
1248            )?;
1249        }
1250        // For pointer typedef aliases (e.g. `typedef SyntaOctetString* Label`),
1251        // `value` is `const Label*` = `SyntaOctetString* const*`.  Dereference once
1252        // to obtain the `SyntaOctetString*` and use the type-specific encode function
1253        // so the correct ASN.1 tag is written to the output.
1254        Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_) => {
1255            writeln!(
1256                output,
1257                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1258            )?;
1259            writeln!(
1260                output,
1261                "    return synta_encode_octet_string(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1262            )?;
1263        }
1264        Type::Utf8String(_) => {
1265            writeln!(
1266                output,
1267                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1268            )?;
1269            writeln!(
1270                output,
1271                "    return synta_encode_utf8_string_bytes(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1272            )?;
1273        }
1274        Type::PrintableString(_) => {
1275            writeln!(
1276                output,
1277                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1278            )?;
1279            writeln!(
1280                output,
1281                "    return synta_encode_printable_string_bytes(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1282            )?;
1283        }
1284        Type::IA5String(_) => {
1285            writeln!(
1286                output,
1287                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1288            )?;
1289            writeln!(
1290                output,
1291                "    return synta_encode_ia5_string_bytes(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1292            )?;
1293        }
1294        Type::UtcTime => {
1295            writeln!(
1296                output,
1297                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1298            )?;
1299            writeln!(
1300                output,
1301                "    return synta_encode_utctime_bytes(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1302            )?;
1303        }
1304        Type::GeneralizedTime => {
1305            writeln!(
1306                output,
1307                "    if (*value == NULL) return SyntaErrorCode_NullPointer;"
1308            )?;
1309            writeln!(
1310                output,
1311                "    return synta_encode_generalized_time_bytes(encoder, synta_octet_string_data(*value), synta_octet_string_len(*value));"
1312            )?;
1313        }
1314        Type::TypeRef(ref_name) => {
1315            let ref_fn = to_snake_case(ref_name);
1316            writeln!(output, "    return {}_encode(encoder, value);", ref_fn)?;
1317        }
1318        _ => {
1319            writeln!(output, "    /* unsupported simple type alias */")?;
1320            writeln!(output, "    return SyntaErrorCode_InvalidEncoding;")?;
1321        }
1322    }
1323    writeln!(output, "}}")?;
1324
1325    Ok(())
1326}
1327
1328/// Build the C API call string to decode `ty` from `decoder` into `var`.
1329///
1330/// Returns `None` for types that require structural handling (SEQUENCE, SEQUENCE OF,
1331/// SET, SET OF) so callers can emit that code explicitly.
1332/// Automatically unwraps `Tagged` and `Constrained` wrappers by recursion.
1333fn make_decode_call(ty: &Type, decoder: &str, var: &str) -> Option<String> {
1334    match ty {
1335        Type::Boolean => Some(format!("synta_decode_boolean({}, &{})", decoder, var)),
1336        Type::Integer(_, _) | Type::Enumerated(_) => {
1337            Some(format!("synta_decode_integer({}, &{})", decoder, var))
1338        }
1339        Type::Real => Some(format!("synta_decode_real({}, &{})", decoder, var)),
1340        Type::Null => Some(format!("synta_decode_null({})", decoder)),
1341        Type::ObjectIdentifier => Some(format!(
1342            "synta_decode_object_identifier({}, &{})",
1343            decoder, var
1344        )),
1345        Type::BitString(_) => Some(format!(
1346            "synta_decode_bit_string({}, &{}.data, &{}.unused_bits)",
1347            decoder, var, var
1348        )),
1349        Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_) => {
1350            Some(format!("synta_decode_octet_string({}, &{})", decoder, var))
1351        }
1352        Type::Utf8String(_) => Some(format!(
1353            "synta_decode_utf8_string_os({}, &{})",
1354            decoder, var
1355        )),
1356        Type::PrintableString(_) => Some(format!(
1357            "synta_decode_printable_string_os({}, &{})",
1358            decoder, var
1359        )),
1360        Type::IA5String(_) => Some(format!("synta_decode_ia5_string_os({}, &{})", decoder, var)),
1361        Type::UtcTime => Some(format!("synta_decode_utctime_os({}, &{})", decoder, var)),
1362        Type::GeneralizedTime => Some(format!(
1363            "synta_decode_generalized_time_os({}, &{})",
1364            decoder, var
1365        )),
1366        Type::TypeRef(name) => Some(format!(
1367            "{}_decode({}, &{})",
1368            to_snake_case(name),
1369            decoder,
1370            var
1371        )),
1372        Type::Tagged { tag, inner } => match tag.tagging {
1373            // EXPLICIT always wraps the inner TLV in a constructed outer TLV;
1374            // return None so the caller falls through to structural handling in
1375            // generate_field_decode where the enter_constructed wrapper is emitted.
1376            Tagging::Explicit => None,
1377            // IMPLICIT: the tag is replaced but the value bytes are unchanged,
1378            // so make a best-effort attempt to decode the inner type directly.
1379            Tagging::Implicit => make_decode_call(inner, decoder, var),
1380        },
1381        Type::Constrained {
1382            base_type: inner, ..
1383        } => make_decode_call(inner, decoder, var),
1384        _ => None,
1385    }
1386}
1387
1388/// Build the C API call string to encode `ty` from `var` into `encoder`.
1389///
1390/// Returns `None` for types that require structural handling.
1391/// Automatically unwraps `Tagged` and `Constrained` wrappers by recursion.
1392fn make_encode_call(ty: &Type, encoder: &str, var: &str) -> Option<String> {
1393    match ty {
1394        Type::Boolean => Some(format!("synta_encode_boolean({}, {})", encoder, var)),
1395        Type::Integer(_, _) | Type::Enumerated(_) => {
1396            Some(format!("synta_encode_integer({}, {})", encoder, var))
1397        }
1398        Type::Real => Some(format!("synta_encode_real({}, {})", encoder, var)),
1399        Type::Null => Some(format!("synta_encode_null({})", encoder)),
1400        Type::ObjectIdentifier => {
1401            Some(format!("synta_encode_object_identifier({}, {})", encoder, var))
1402        }
1403        Type::BitString(_) => Some(format!(
1404            "synta_encode_bit_string({}, {}.data.data, {}.data.len, {}.unused_bits)",
1405            encoder, var, var, var
1406        )),
1407        Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_) => Some(format!(
1408            "synta_encode_octet_string({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1409            encoder, var, var
1410        )),
1411        Type::Utf8String(_) => Some(format!(
1412            "synta_encode_utf8_string_bytes({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1413            encoder, var, var
1414        )),
1415        Type::PrintableString(_) => Some(format!(
1416            "synta_encode_printable_string_bytes({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1417            encoder, var, var
1418        )),
1419        Type::IA5String(_) => Some(format!(
1420            "synta_encode_ia5_string_bytes({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1421            encoder, var, var
1422        )),
1423        Type::UtcTime => Some(format!(
1424            "synta_encode_utctime_bytes({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1425            encoder, var, var
1426        )),
1427        Type::GeneralizedTime => Some(format!(
1428            "synta_encode_generalized_time_bytes({}, synta_octet_string_data({}), synta_octet_string_len({}))",
1429            encoder, var, var
1430        )),
1431        Type::TypeRef(name) => Some(format!(
1432            "{}_encode({}, &{})",
1433            to_snake_case(name),
1434            encoder,
1435            var
1436        )),
1437        Type::Tagged { tag, inner } => match tag.tagging {
1438            Tagging::Explicit => None,
1439            Tagging::Implicit => make_encode_call(inner, encoder, var),
1440        },
1441        Type::Constrained { base_type: inner, .. } => make_encode_call(inner, encoder, var),
1442        _ => None,
1443    }
1444}
1445
1446/// Generate C code to decode one field of a SEQUENCE/SET into `var_name`.
1447///
1448/// **Caller contract** — the generated code assumes the following variables are
1449/// already declared and in scope at the call site:
1450/// - `SyntaErrorCode err` — reused for every decode call
1451/// - `SyntaDecoder* seq_decoder` — the decoder for the enclosing SEQUENCE/SET;
1452///   freed and returned on error
1453///
1454/// On decode error, the generated code frees `seq_decoder` and returns `err`.
1455/// Fields that were successfully decoded before the failure are **not** freed;
1456/// callers that want clean-up on partial decode must call the corresponding
1457/// `_free` function even when this returns an error.
1458fn generate_field_decode(
1459    output: &mut String,
1460    ty: &Type,
1461    var_name: &str,
1462    indent: usize,
1463    defs: &[Definition],
1464) -> Result<(), Box<dyn std::error::Error>> {
1465    let ind = "    ".repeat(indent);
1466
1467    // For IMPLICIT-tagged fields, check whether the concrete inner type is structural
1468    // (SEQUENCE, SET, SEQUENCE OF, SET OF via TypeRef).  If so, generate inline
1469    // decode with context-specific constructed TLV entry and return early, before
1470    // make_decode_call gets a chance to emit a call to the TypeRef's own decoder
1471    // (which would use the wrong tag).  For primitive inner types, fall through to
1472    // the make_decode_call path below with a transparency comment.
1473    if let Type::Tagged {
1474        tag: tag_info,
1475        inner,
1476    } = ty
1477    {
1478        if matches!(tag_info.tagging, Tagging::Implicit) {
1479            let concrete = resolve_to_base(inner, defs);
1480            match concrete {
1481                Type::Sequence(_) | Type::Set(_) | Type::SequenceOf(_, _) | Type::SetOf(_, _) => {
1482                    // Delegate to the structural handler in the else-branch below.
1483                    // We fall through to `make_decode_call` which returns None for
1484                    // these structural types, so the else-branch handles them.
1485                    // But make_decode_call on a Tagged{Implicit, TypeRef} would
1486                    // return Some (the TypeRef's decode call), so we must bypass it.
1487                    // Recurse with the concrete type and var_name substituted so
1488                    // that generate_field_decode re-enters as the Tagged variant
1489                    // and falls into the else -> Tagging::Implicit -> structural arm.
1490                    //
1491                    // Actually: pass through the Tagged ty itself but call the new
1492                    // inline code path directly via the else-branch by temporarily
1493                    // preventing make_decode_call from short-circuiting. The cleanest
1494                    // approach is to inline the structural handling here.
1495                    let class = tag_class_c_str(&tag_info.class);
1496                    match concrete {
1497                        Type::Sequence(seq_fields) | Type::Set(seq_fields) => {
1498                            let is_set = matches!(concrete, Type::Set(_));
1499                            let ct_label = if is_set { "SET" } else { "SEQUENCE" };
1500                            writeln!(
1501                                output,
1502                                "{}// IMPLICIT [{}]: enter as CONSTRUCTED, decode {} fields inline",
1503                                ind, tag_info.number, ct_label
1504                            )?;
1505                            writeln!(output, "{}SyntaDecoder* nested_decoder = NULL;", ind)?;
1506                            writeln!(output, "{}{{", ind)?;
1507                            writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
1508                            writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
1509                            writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
1510                            writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
1511                            writeln!(
1512                                output,
1513                                "{}    err = synta_decoder_enter_constructed(seq_decoder, impl_tag, &nested_decoder);",
1514                                ind
1515                            )?;
1516                            writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
1517                            writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
1518                            writeln!(output, "{}        return err;", ind)?;
1519                            writeln!(output, "{}    }}", ind)?;
1520                            writeln!(output, "{}}}", ind)?;
1521                            writeln!(output)?;
1522                            for seq_field in seq_fields {
1523                                if matches!(seq_field.ty, Type::Null) {
1524                                    continue;
1525                                }
1526                                let inner_name = to_snake_case(&seq_field.name);
1527                                let nested_var = format!("{}.{}", var_name, inner_name);
1528                                if seq_field.optional {
1529                                    let tag_check = get_tag_check_condition(&seq_field.ty, defs);
1530                                    writeln!(
1531                                        output,
1532                                        "{}// Decode optional nested field: {}",
1533                                        ind, seq_field.name
1534                                    )?;
1535                                    writeln!(output, "{}{{", ind)?;
1536                                    writeln!(output, "{}    SyntaTag tag;", ind)?;
1537                                    writeln!(
1538                                        output,
1539                                        "{}    if (synta_decoder_peek_tag(nested_decoder, &tag) == SyntaErrorCode_Success &&",
1540                                        ind
1541                                    )?;
1542                                    writeln!(output, "{}        ({})) {{", ind, tag_check)?;
1543                                    let deeper_ind = format!("{}        ", ind);
1544                                    generate_nested_inline_field_decode(
1545                                        output,
1546                                        &seq_field.ty,
1547                                        &nested_var,
1548                                        &deeper_ind,
1549                                    )?;
1550                                    writeln!(
1551                                        output,
1552                                        "{}        {}.has_{} = true;",
1553                                        ind, var_name, inner_name
1554                                    )?;
1555                                    writeln!(output, "{}    }} else {{", ind)?;
1556                                    writeln!(
1557                                        output,
1558                                        "{}        {}.has_{} = false;",
1559                                        ind, var_name, inner_name
1560                                    )?;
1561                                    writeln!(output, "{}    }}", ind)?;
1562                                    writeln!(output, "{}}}", ind)?;
1563                                } else {
1564                                    writeln!(output, "{}// Decode field: {}", ind, seq_field.name)?;
1565                                    generate_nested_inline_field_decode(
1566                                        output,
1567                                        &seq_field.ty,
1568                                        &nested_var,
1569                                        &ind,
1570                                    )?;
1571                                }
1572                                writeln!(output)?;
1573                            }
1574                            writeln!(output, "{}synta_decoder_free(nested_decoder);", ind)?;
1575                            return Ok(());
1576                        }
1577                        Type::SequenceOf(elem_ty, _) | Type::SetOf(elem_ty, _) => {
1578                            let is_seq = matches!(concrete, Type::SequenceOf(_, _));
1579                            let ct_label = if is_seq { "SEQUENCE OF" } else { "SET OF" };
1580                            writeln!(
1581                                output,
1582                                "{}// IMPLICIT [{}]: enter as CONSTRUCTED, decode {} elements inline",
1583                                ind, tag_info.number, ct_label
1584                            )?;
1585                            writeln!(output, "{}SyntaDecoder* array_decoder = NULL;", ind)?;
1586                            writeln!(output, "{}{{", ind)?;
1587                            writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
1588                            writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
1589                            writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
1590                            writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
1591                            writeln!(
1592                                output,
1593                                "{}    err = synta_decoder_enter_constructed(seq_decoder, impl_tag, &array_decoder);",
1594                                ind
1595                            )?;
1596                            writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
1597                            writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
1598                            writeln!(output, "{}        return err;", ind)?;
1599                            writeln!(output, "{}    }}", ind)?;
1600                            writeln!(output, "{}}}", ind)?;
1601                            writeln!(output)?;
1602                            let elem_c_type = get_c_type(elem_ty);
1603                            let array_type = format!("{}*", elem_c_type);
1604                            writeln!(output, "{}size_t capacity = 4;", ind)?;
1605                            writeln!(output, "{}size_t count = 0;", ind)?;
1606                            writeln!(
1607                                output,
1608                                "{}{} array = ({})calloc(capacity, sizeof({}));",
1609                                ind, array_type, array_type, elem_c_type
1610                            )?;
1611                            writeln!(output, "{}if (array == NULL) {{", ind)?;
1612                            writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
1613                            writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1614                            writeln!(output, "{}    return SyntaErrorCode_OutOfMemory;", ind)?;
1615                            writeln!(output, "{}}}", ind)?;
1616                            writeln!(output)?;
1617                            writeln!(
1618                                output,
1619                                "{}while (!synta_decoder_at_end(array_decoder)) {{",
1620                                ind
1621                            )?;
1622                            writeln!(output, "{}    if (count >= capacity) {{", ind)?;
1623                            writeln!(output, "{}        capacity *= 2;", ind)?;
1624                            writeln!(
1625                                output,
1626                                "{}        {} new_array = ({})realloc(array, capacity * sizeof({}));",
1627                                ind, array_type, array_type, elem_c_type
1628                            )?;
1629                            writeln!(output, "{}        if (new_array == NULL) {{", ind)?;
1630                            writeln!(output, "{}            free(array);", ind)?;
1631                            writeln!(
1632                                output,
1633                                "{}            synta_decoder_free(array_decoder);",
1634                                ind
1635                            )?;
1636                            writeln!(
1637                                output,
1638                                "{}            synta_decoder_free(seq_decoder);",
1639                                ind
1640                            )?;
1641                            writeln!(
1642                                output,
1643                                "{}            return SyntaErrorCode_OutOfMemory;",
1644                                ind
1645                            )?;
1646                            writeln!(output, "{}        }}", ind)?;
1647                            writeln!(output, "{}        array = new_array;", ind)?;
1648                            writeln!(output, "{}    }}", ind)?;
1649                            writeln!(output)?;
1650                            generate_decode_element(
1651                                output,
1652                                elem_ty,
1653                                "array_decoder",
1654                                "array[count]",
1655                                "    ",
1656                            )?;
1657                            writeln!(output, "{}    count++;", ind)?;
1658                            writeln!(output, "{}}}", ind)?;
1659                            writeln!(output)?;
1660                            writeln!(output, "{}{}.items = array;", ind, var_name)?;
1661                            writeln!(output, "{}{}.count = count;", ind, var_name)?;
1662                            writeln!(output, "{}synta_decoder_free(array_decoder);", ind)?;
1663                            return Ok(());
1664                        }
1665                        _ => unreachable!(),
1666                    }
1667                }
1668                _ => {
1669                    // Primitive inner type: fall through to make_decode_call with a
1670                    // transparency comment.
1671                    let cls = match tag_info.class {
1672                        TagClass::ContextSpecific => tag_info.number.to_string(),
1673                        TagClass::Application => format!("APPLICATION {}", tag_info.number),
1674                        TagClass::Universal => format!("UNIVERSAL {}", tag_info.number),
1675                        TagClass::Private => format!("PRIVATE {}", tag_info.number),
1676                    };
1677                    writeln!(
1678                        output,
1679                        "{}/* IMPLICIT [{}]: primitive inner type decoded directly (tag check relaxed) */",
1680                        ind, cls
1681                    )?;
1682                }
1683            }
1684        }
1685    }
1686
1687    if let Some(call) = make_decode_call(ty, "seq_decoder", var_name) {
1688        writeln!(output, "{}err = {};", ind, call)?;
1689        writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
1690        writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1691        writeln!(output, "{}    return err;", ind)?;
1692        writeln!(output, "{}}}", ind)?;
1693    } else {
1694        match ty {
1695            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
1696                let is_sequence = matches!(ty, Type::Sequence(_));
1697                let container_type = if is_sequence { "sequence" } else { "set" };
1698
1699                writeln!(
1700                    output,
1701                    "{}// Decode nested {}",
1702                    ind,
1703                    container_type.to_uppercase()
1704                )?;
1705                writeln!(output, "{}SyntaDecoder* nested_decoder = NULL;", ind)?;
1706                writeln!(
1707                    output,
1708                    "{}err = synta_decoder_enter_{}(seq_decoder, &nested_decoder);",
1709                    ind, container_type
1710                )?;
1711                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
1712                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1713                writeln!(output, "{}    return err;", ind)?;
1714                writeln!(output, "{}}}", ind)?;
1715                writeln!(output)?;
1716
1717                // Decode each field of the nested sequence
1718                for inner_field in inner_fields {
1719                    if matches!(inner_field.ty, Type::Null) {
1720                        continue;
1721                    }
1722
1723                    let inner_name = to_snake_case(&inner_field.name);
1724                    let nested_var = format!("{}.{}", var_name, inner_name);
1725                    if inner_field.optional {
1726                        writeln!(
1727                            output,
1728                            "{}// Decode optional nested field: {}",
1729                            ind, inner_field.name
1730                        )?;
1731                        let tag_check = get_tag_check_condition(&inner_field.ty, defs);
1732                        writeln!(output, "{}{{", ind)?;
1733                        writeln!(output, "{}    SyntaTag tag;", ind)?;
1734                        writeln!(
1735                        output,
1736                        "{}    if (synta_decoder_peek_tag(nested_decoder, &tag) == SyntaErrorCode_Success &&",
1737                        ind
1738                    )?;
1739                        writeln!(output, "{}        ({})) {{", ind, tag_check)?;
1740                        let deeper_ind = format!("{}        ", ind);
1741                        generate_nested_inline_field_decode(
1742                            output,
1743                            &inner_field.ty,
1744                            &nested_var,
1745                            &deeper_ind,
1746                        )?;
1747                        writeln!(
1748                            output,
1749                            "{}        {}.has_{} = true;",
1750                            ind, var_name, inner_name
1751                        )?;
1752                        writeln!(output, "{}    }} else {{", ind)?;
1753                        writeln!(
1754                            output,
1755                            "{}        {}.has_{} = false;",
1756                            ind, var_name, inner_name
1757                        )?;
1758                        writeln!(output, "{}    }}", ind)?;
1759                        writeln!(output, "{}}}", ind)?;
1760                    } else {
1761                        writeln!(output, "{}// Decode field: {}", ind, inner_field.name)?;
1762                        generate_nested_inline_field_decode(
1763                            output,
1764                            &inner_field.ty,
1765                            &nested_var,
1766                            &ind,
1767                        )?;
1768                    }
1769                    writeln!(output)?;
1770                }
1771
1772                writeln!(output, "{}synta_decoder_free(nested_decoder);", ind)?;
1773            }
1774            Type::SequenceOf(inner_ty, _) | Type::SetOf(inner_ty, _) => {
1775                let is_sequence = matches!(ty, Type::SequenceOf(_, _));
1776                let container_type = if is_sequence { "sequence" } else { "set" };
1777
1778                writeln!(output, "{}// Decode SEQUENCE OF / SET OF", ind)?;
1779                writeln!(output, "{}SyntaDecoder* array_decoder = NULL;", ind)?;
1780                writeln!(
1781                    output,
1782                    "{}err = synta_decoder_enter_{}(seq_decoder, &array_decoder);",
1783                    ind, container_type
1784                )?;
1785                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
1786                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1787                writeln!(output, "{}    return err;", ind)?;
1788                writeln!(output, "{}}}", ind)?;
1789                writeln!(output)?;
1790
1791                writeln!(output, "{}// Allocate dynamic array for elements", ind)?;
1792                writeln!(output, "{}size_t capacity = 4; // Initial capacity", ind)?;
1793                writeln!(output, "{}size_t count = 0;", ind)?;
1794                let elem_c_type = get_c_type(inner_ty);
1795                let array_type = format!("{}*", elem_c_type); // Array of element pointers
1796                writeln!(
1797                    output,
1798                    "{}{} array = ({})calloc(capacity, sizeof({}));",
1799                    ind, array_type, array_type, elem_c_type
1800                )?;
1801                writeln!(output, "{}if (array == NULL) {{", ind)?;
1802                writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
1803                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1804                writeln!(output, "{}    return SyntaErrorCode_OutOfMemory;", ind)?;
1805                writeln!(output, "{}}}", ind)?;
1806                writeln!(output)?;
1807
1808                writeln!(
1809                    output,
1810                    "{}while (!synta_decoder_at_end(array_decoder)) {{",
1811                    ind
1812                )?;
1813                writeln!(output, "{}    // Grow array if needed", ind)?;
1814                writeln!(output, "{}    if (count >= capacity) {{", ind)?;
1815                writeln!(output, "{}        capacity *= 2;", ind)?;
1816                writeln!(
1817                    output,
1818                    "{}        {} new_array = ({})realloc(array, capacity * sizeof({}));",
1819                    ind, array_type, array_type, elem_c_type
1820                )?;
1821                writeln!(output, "{}        if (new_array == NULL) {{", ind)?;
1822                writeln!(output, "{}            free(array);", ind)?;
1823                writeln!(
1824                    output,
1825                    "{}            synta_decoder_free(array_decoder);",
1826                    ind
1827                )?;
1828                writeln!(
1829                    output,
1830                    "{}            synta_decoder_free(seq_decoder);",
1831                    ind
1832                )?;
1833                writeln!(
1834                    output,
1835                    "{}            return SyntaErrorCode_OutOfMemory;",
1836                    ind
1837                )?;
1838                writeln!(output, "{}        }}", ind)?;
1839                writeln!(output, "{}        array = new_array;", ind)?;
1840                writeln!(output, "{}    }}", ind)?;
1841                writeln!(output)?;
1842
1843                // Generate decode for element
1844                writeln!(output, "{}    // Decode element", ind)?;
1845                generate_decode_element(output, inner_ty, "array_decoder", "array[count]", "    ")?;
1846                writeln!(output, "{}    count++;", ind)?;
1847                writeln!(output, "{}}}", ind)?;
1848                writeln!(output)?;
1849
1850                writeln!(output, "{}{} = array;", ind, var_name)?;
1851                writeln!(output, "{}{}_count = count;", ind, var_name)?;
1852                writeln!(output, "{}synta_decoder_free(array_decoder);", ind)?;
1853            }
1854            // make_decode_call returned None for a Tagged type: must be EXPLICIT
1855            // (IMPLICIT would have returned Some via recursion into the inner type).
1856            Type::Tagged {
1857                tag: tag_info,
1858                inner,
1859            } => match tag_info.tagging {
1860                Tagging::Explicit => {
1861                    let class = tag_class_c_str(&tag_info.class);
1862                    writeln!(
1863                        output,
1864                        "{}// Enter EXPLICIT [{cls} {num}] tag wrapper",
1865                        ind,
1866                        cls = class,
1867                        num = tag_info.number
1868                    )?;
1869                    writeln!(output, "{}{{", ind)?;
1870                    writeln!(output, "{}    SyntaDecoder* tagged_decoder = NULL;", ind)?;
1871                    writeln!(output, "{}    SyntaTag explicit_tag;", ind)?;
1872                    writeln!(output, "{}    explicit_tag.class_ = {};", ind, class)?;
1873                    writeln!(output, "{}    explicit_tag.constructed = true;", ind)?;
1874                    writeln!(
1875                        output,
1876                        "{}    explicit_tag.number = {};",
1877                        ind, tag_info.number
1878                    )?;
1879                    writeln!(
1880                        output,
1881                        "{}    err = synta_decoder_enter_constructed(seq_decoder, explicit_tag, &tagged_decoder);",
1882                        ind
1883                    )?;
1884                    writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
1885                    writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
1886                    writeln!(output, "{}        return err;", ind)?;
1887                    writeln!(output, "{}    }}", ind)?;
1888                    if let Some(call) = make_decode_call(inner, "tagged_decoder", var_name) {
1889                        writeln!(output, "{}    err = {};", ind, call)?;
1890                        writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
1891                        writeln!(output, "{}        synta_decoder_free(tagged_decoder);", ind)?;
1892                        writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
1893                        writeln!(output, "{}        return err;", ind)?;
1894                        writeln!(output, "{}    }}", ind)?;
1895                    } else {
1896                        // Inline structural type (e.g. anonymous SEQUENCE) inside an
1897                        // EXPLICIT tag is not yet supported; emit an error placeholder.
1898                        writeln!(output, "{}    /* structural inner type inside EXPLICIT tag: not yet supported */", ind)?;
1899                        writeln!(output, "{}    err = SyntaErrorCode_InvalidEncoding;", ind)?;
1900                        writeln!(output, "{}    synta_decoder_free(tagged_decoder);", ind)?;
1901                        writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
1902                        writeln!(output, "{}    return err;", ind)?;
1903                    }
1904                    writeln!(output, "{}    synta_decoder_free(tagged_decoder);", ind)?;
1905                    writeln!(output, "{}}}", ind)?;
1906                }
1907                Tagging::Implicit => {
1908                    // IMPLICIT: context-specific tag replaces the inner type's own tag.
1909                    // For structural inner types (SEQUENCE, SET, SEQUENCE OF, SET OF or
1910                    // TypeRefs that resolve to them), enter the context-specific constructed
1911                    // TLV and decode the contents inline.  For primitive inner types the
1912                    // inner-type decoder reads value bytes without rechecking the outer tag,
1913                    // so we can call it directly.
1914                    let concrete = resolve_to_base(inner, defs);
1915                    match concrete {
1916                        Type::Sequence(seq_fields) | Type::Set(seq_fields) => {
1917                            let class = tag_class_c_str(&tag_info.class);
1918                            let is_set = matches!(concrete, Type::Set(_));
1919                            let ct_label = if is_set { "SET" } else { "SEQUENCE" };
1920                            writeln!(
1921                                output,
1922                                "{}// IMPLICIT [{}]: enter as CONSTRUCTED, decode {} fields inline",
1923                                ind, tag_info.number, ct_label
1924                            )?;
1925                            writeln!(output, "{}SyntaDecoder* nested_decoder = NULL;", ind)?;
1926                            writeln!(output, "{}{{", ind)?;
1927                            writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
1928                            writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
1929                            writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
1930                            writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
1931                            writeln!(
1932                                output,
1933                                "{}    err = synta_decoder_enter_constructed(seq_decoder, impl_tag, &nested_decoder);",
1934                                ind
1935                            )?;
1936                            writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
1937                            writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
1938                            writeln!(output, "{}        return err;", ind)?;
1939                            writeln!(output, "{}    }}", ind)?;
1940                            writeln!(output, "{}}}", ind)?;
1941                            writeln!(output)?;
1942                            for seq_field in seq_fields {
1943                                if matches!(seq_field.ty, Type::Null) {
1944                                    continue;
1945                                }
1946                                let inner_name = to_snake_case(&seq_field.name);
1947                                let nested_var = format!("{}.{}", var_name, inner_name);
1948                                if seq_field.optional {
1949                                    let tag_check = get_tag_check_condition(&seq_field.ty, defs);
1950                                    writeln!(
1951                                        output,
1952                                        "{}// Decode optional nested field: {}",
1953                                        ind, seq_field.name
1954                                    )?;
1955                                    writeln!(output, "{}{{", ind)?;
1956                                    writeln!(output, "{}    SyntaTag tag;", ind)?;
1957                                    writeln!(
1958                                        output,
1959                                        "{}    if (synta_decoder_peek_tag(nested_decoder, &tag) == SyntaErrorCode_Success &&",
1960                                        ind
1961                                    )?;
1962                                    writeln!(output, "{}        ({})) {{", ind, tag_check)?;
1963                                    let deeper_ind = format!("{}        ", ind);
1964                                    generate_nested_inline_field_decode(
1965                                        output,
1966                                        &seq_field.ty,
1967                                        &nested_var,
1968                                        &deeper_ind,
1969                                    )?;
1970                                    writeln!(
1971                                        output,
1972                                        "{}        {}.has_{} = true;",
1973                                        ind, var_name, inner_name
1974                                    )?;
1975                                    writeln!(output, "{}    }} else {{", ind)?;
1976                                    writeln!(
1977                                        output,
1978                                        "{}        {}.has_{} = false;",
1979                                        ind, var_name, inner_name
1980                                    )?;
1981                                    writeln!(output, "{}    }}", ind)?;
1982                                    writeln!(output, "{}}}", ind)?;
1983                                } else {
1984                                    writeln!(output, "{}// Decode field: {}", ind, seq_field.name)?;
1985                                    generate_nested_inline_field_decode(
1986                                        output,
1987                                        &seq_field.ty,
1988                                        &nested_var,
1989                                        &ind,
1990                                    )?;
1991                                }
1992                                writeln!(output)?;
1993                            }
1994                            writeln!(output, "{}synta_decoder_free(nested_decoder);", ind)?;
1995                        }
1996                        Type::SequenceOf(elem_ty, _) | Type::SetOf(elem_ty, _) => {
1997                            let class = tag_class_c_str(&tag_info.class);
1998                            let is_seq = matches!(concrete, Type::SequenceOf(_, _));
1999                            let ct_label = if is_seq { "SEQUENCE OF" } else { "SET OF" };
2000                            writeln!(
2001                                output,
2002                                "{}// IMPLICIT [{}]: enter as CONSTRUCTED, decode {} elements inline",
2003                                ind, tag_info.number, ct_label
2004                            )?;
2005                            writeln!(output, "{}SyntaDecoder* array_decoder = NULL;", ind)?;
2006                            writeln!(output, "{}{{", ind)?;
2007                            writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
2008                            writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
2009                            writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
2010                            writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
2011                            writeln!(
2012                                output,
2013                                "{}    err = synta_decoder_enter_constructed(seq_decoder, impl_tag, &array_decoder);",
2014                                ind
2015                            )?;
2016                            writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
2017                            writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
2018                            writeln!(output, "{}        return err;", ind)?;
2019                            writeln!(output, "{}    }}", ind)?;
2020                            writeln!(output, "{}}}", ind)?;
2021                            writeln!(output)?;
2022                            let elem_c_type = get_c_type(elem_ty);
2023                            let array_type = format!("{}*", elem_c_type);
2024                            writeln!(output, "{}size_t capacity = 4;", ind)?;
2025                            writeln!(output, "{}size_t count = 0;", ind)?;
2026                            writeln!(
2027                                output,
2028                                "{}{} array = ({})calloc(capacity, sizeof({}));",
2029                                ind, array_type, array_type, elem_c_type
2030                            )?;
2031                            writeln!(output, "{}if (array == NULL) {{", ind)?;
2032                            writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
2033                            writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
2034                            writeln!(output, "{}    return SyntaErrorCode_OutOfMemory;", ind)?;
2035                            writeln!(output, "{}}}", ind)?;
2036                            writeln!(output)?;
2037                            writeln!(
2038                                output,
2039                                "{}while (!synta_decoder_at_end(array_decoder)) {{",
2040                                ind
2041                            )?;
2042                            writeln!(output, "{}    if (count >= capacity) {{", ind)?;
2043                            writeln!(output, "{}        capacity *= 2;", ind)?;
2044                            writeln!(
2045                                output,
2046                                "{}        {} new_array = ({})realloc(array, capacity * sizeof({}));",
2047                                ind, array_type, array_type, elem_c_type
2048                            )?;
2049                            writeln!(output, "{}        if (new_array == NULL) {{", ind)?;
2050                            writeln!(output, "{}            free(array);", ind)?;
2051                            writeln!(
2052                                output,
2053                                "{}            synta_decoder_free(array_decoder);",
2054                                ind
2055                            )?;
2056                            writeln!(
2057                                output,
2058                                "{}            synta_decoder_free(seq_decoder);",
2059                                ind
2060                            )?;
2061                            writeln!(
2062                                output,
2063                                "{}            return SyntaErrorCode_OutOfMemory;",
2064                                ind
2065                            )?;
2066                            writeln!(output, "{}        }}", ind)?;
2067                            writeln!(output, "{}        array = new_array;", ind)?;
2068                            writeln!(output, "{}    }}", ind)?;
2069                            writeln!(output)?;
2070                            generate_decode_element(
2071                                output,
2072                                elem_ty,
2073                                "array_decoder",
2074                                "array[count]",
2075                                "    ",
2076                            )?;
2077                            writeln!(output, "{}    count++;", ind)?;
2078                            writeln!(output, "{}}}", ind)?;
2079                            writeln!(output)?;
2080                            writeln!(output, "{}{}.items = array;", ind, var_name)?;
2081                            writeln!(output, "{}{}.count = count;", ind, var_name)?;
2082                            writeln!(output, "{}synta_decoder_free(array_decoder);", ind)?;
2083                        }
2084                        _ => {
2085                            // Primitive or scalar inner type: decode without re-entering a tag wrapper.
2086                            generate_field_decode(output, inner, var_name, indent, defs)?;
2087                        }
2088                    }
2089                }
2090            },
2091            Type::Constrained {
2092                base_type: inner, ..
2093            } => {
2094                generate_field_decode(output, inner, var_name, indent, defs)?;
2095            }
2096            _ => {
2097                writeln!(
2098                    output,
2099                    "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported field type */",
2100                    ind
2101                )?;
2102                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
2103                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
2104                writeln!(output, "{}    return err;", ind)?;
2105                writeln!(output, "{}}}", ind)?;
2106            }
2107        }
2108    } // closes else
2109
2110    Ok(())
2111}
2112
2113/// Generate C code to encode one field of a SEQUENCE/SET from `var_name`.
2114///
2115/// **Caller contract** — the generated code assumes the following variables are
2116/// already declared and in scope at the call site:
2117/// - `SyntaErrorCode err` — reused for every encode call
2118/// - A `SyntaEncoder*` whose name is passed as `encoder_name`
2119///
2120/// On encode error, the generated code returns `err` immediately.
2121fn generate_field_encode(
2122    output: &mut String,
2123    ty: &Type,
2124    var_name: &str,
2125    encoder_name: &str,
2126    indent: usize,
2127    ctx: &EncodeCtx<'_>,
2128) -> Result<(), Box<dyn std::error::Error>> {
2129    let ind = "    ".repeat(indent);
2130
2131    // Constrained type: emit validation before the actual encode, then encode inner.
2132    if let Type::Constrained {
2133        base_type: inner,
2134        constraint,
2135    } = ty
2136    {
2137        emit_constraint_validation(
2138            output,
2139            constraint,
2140            inner,
2141            var_name,
2142            &ind,
2143            ctx.mode,
2144            ctx.with_containing,
2145        )?;
2146        return generate_field_encode(output, inner, var_name, encoder_name, indent, ctx);
2147    }
2148
2149    // For IMPLICIT-tagged structural types (SEQUENCE, SET, SEQUENCE OF, SET OF or
2150    // TypeRefs resolving to them), generate context-specific constructed TLV encode
2151    // inline instead of calling the TypeRef's encode function (which would emit the
2152    // wrong outer tag).
2153    if let Type::Tagged {
2154        tag: tag_info,
2155        inner,
2156    } = ty
2157    {
2158        if matches!(tag_info.tagging, Tagging::Implicit) {
2159            let concrete = resolve_to_base(inner, ctx.defs);
2160            match concrete {
2161                Type::Sequence(seq_fields) | Type::Set(seq_fields) => {
2162                    let class = tag_class_c_str(&tag_info.class);
2163                    let is_set = matches!(concrete, Type::Set(_));
2164                    let ct_label = if is_set { "SET" } else { "SEQUENCE" };
2165                    writeln!(
2166                        output,
2167                        "{}// IMPLICIT [{}]: start CONSTRUCTED, encode {} fields inline",
2168                        ind, tag_info.number, ct_label
2169                    )?;
2170                    writeln!(output, "{}SyntaEncoder* nested_encoder = NULL;", ind)?;
2171                    writeln!(output, "{}{{", ind)?;
2172                    writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
2173                    writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
2174                    writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
2175                    writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
2176                    writeln!(
2177                        output,
2178                        "{}    err = synta_encoder_start_constructed({}, impl_tag, &nested_encoder);",
2179                        ind, encoder_name
2180                    )?;
2181                    writeln!(
2182                        output,
2183                        "{}    if (err != SyntaErrorCode_Success) return err;",
2184                        ind
2185                    )?;
2186                    writeln!(output, "{}}}", ind)?;
2187                    for seq_field in seq_fields {
2188                        if matches!(seq_field.ty, Type::Null) {
2189                            continue;
2190                        }
2191                        let inner_name = to_snake_case(&seq_field.name);
2192                        let nested_var = format!("{}.{}", var_name, inner_name);
2193                        let field_ind = if seq_field.optional {
2194                            writeln!(output, "{}if ({}.has_{}) {{", ind, var_name, inner_name)?;
2195                            format!("{}    ", ind)
2196                        } else {
2197                            ind.clone()
2198                        };
2199                        generate_nested_inline_field_encode(
2200                            output,
2201                            &seq_field.ty,
2202                            &nested_var,
2203                            &field_ind,
2204                        )?;
2205                        if seq_field.optional {
2206                            writeln!(output, "{}}}", ind)?;
2207                        }
2208                    }
2209                    writeln!(output)?;
2210                    writeln!(
2211                        output,
2212                        "{}err = synta_encoder_end_constructed(nested_encoder);",
2213                        ind
2214                    )?;
2215                    writeln!(
2216                        output,
2217                        "{}if (err != SyntaErrorCode_Success) return err;",
2218                        ind
2219                    )?;
2220                    return Ok(());
2221                }
2222                Type::SequenceOf(elem_ty, _) | Type::SetOf(elem_ty, _) => {
2223                    let class = tag_class_c_str(&tag_info.class);
2224                    let is_seq = matches!(concrete, Type::SequenceOf(_, _));
2225                    let ct_label = if is_seq { "SEQUENCE OF" } else { "SET OF" };
2226                    writeln!(
2227                        output,
2228                        "{}// IMPLICIT [{}]: start CONSTRUCTED, encode {} elements inline",
2229                        ind, tag_info.number, ct_label
2230                    )?;
2231                    writeln!(output, "{}SyntaEncoder* array_encoder = NULL;", ind)?;
2232                    writeln!(output, "{}{{", ind)?;
2233                    writeln!(output, "{}    SyntaTag impl_tag;", ind)?;
2234                    writeln!(output, "{}    impl_tag.class_ = {};", ind, class)?;
2235                    writeln!(output, "{}    impl_tag.constructed = true;", ind)?;
2236                    writeln!(output, "{}    impl_tag.number = {};", ind, tag_info.number)?;
2237                    writeln!(
2238                        output,
2239                        "{}    err = synta_encoder_start_constructed({}, impl_tag, &array_encoder);",
2240                        ind, encoder_name
2241                    )?;
2242                    writeln!(
2243                        output,
2244                        "{}    if (err != SyntaErrorCode_Success) return err;",
2245                        ind
2246                    )?;
2247                    writeln!(output, "{}}}", ind)?;
2248                    writeln!(output)?;
2249                    writeln!(
2250                        output,
2251                        "{}for (size_t i = 0; i < {}.count; i++) {{",
2252                        ind, var_name
2253                    )?;
2254                    generate_encode_element_field(
2255                        output,
2256                        elem_ty,
2257                        "array_encoder",
2258                        &format!("{}.items[i]", var_name),
2259                        "    ",
2260                    )?;
2261                    writeln!(output, "{}}}", ind)?;
2262                    writeln!(output)?;
2263                    writeln!(
2264                        output,
2265                        "{}err = synta_encoder_end_constructed(array_encoder);",
2266                        ind
2267                    )?;
2268                    writeln!(
2269                        output,
2270                        "{}if (err != SyntaErrorCode_Success) return err;",
2271                        ind
2272                    )?;
2273                    return Ok(());
2274                }
2275                _ => {
2276                    // Primitive: emit comment and fall through to make_encode_call below.
2277                    let cls = match tag_info.class {
2278                        TagClass::ContextSpecific => tag_info.number.to_string(),
2279                        TagClass::Application => format!("APPLICATION {}", tag_info.number),
2280                        TagClass::Universal => format!("UNIVERSAL {}", tag_info.number),
2281                        TagClass::Private => format!("PRIVATE {}", tag_info.number),
2282                    };
2283                    writeln!(
2284                        output,
2285                        "{}/* IMPLICIT [{}]: primitive inner type encoded directly */",
2286                        ind, cls
2287                    )?;
2288                }
2289            }
2290        }
2291    }
2292
2293    if let Some(call) = make_encode_call(ty, encoder_name, var_name) {
2294        writeln!(output, "{}err = {};", ind, call)?;
2295        writeln!(
2296            output,
2297            "{}if (err != SyntaErrorCode_Success) return err;",
2298            ind
2299        )?;
2300    } else {
2301        match ty {
2302            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
2303                let is_sequence = matches!(ty, Type::Sequence(_));
2304                let container_type = if is_sequence { "sequence" } else { "set" };
2305
2306                writeln!(
2307                    output,
2308                    "{}// Encode nested {}",
2309                    ind,
2310                    container_type.to_uppercase()
2311                )?;
2312                writeln!(output, "{}SyntaEncoder* nested_encoder = NULL;", ind)?;
2313                writeln!(
2314                    output,
2315                    "{}err = synta_encoder_start_{}({}, &nested_encoder);",
2316                    ind, container_type, encoder_name
2317                )?;
2318                writeln!(
2319                    output,
2320                    "{}if (err != SyntaErrorCode_Success) return err;",
2321                    ind
2322                )?;
2323                writeln!(output)?;
2324
2325                // Encode each field of the nested sequence
2326                for inner_field in inner_fields {
2327                    if matches!(inner_field.ty, Type::Null) {
2328                        continue;
2329                    }
2330
2331                    let inner_name = to_snake_case(&inner_field.name);
2332                    let nested_var = format!("{}.{}", var_name, inner_name);
2333                    let field_ind = if inner_field.optional {
2334                        writeln!(output, "{}if ({}.has_{}) {{", ind, var_name, inner_name)?;
2335                        format!("{}    ", ind)
2336                    } else {
2337                        ind.clone()
2338                    };
2339                    generate_nested_inline_field_encode(
2340                        output,
2341                        &inner_field.ty,
2342                        &nested_var,
2343                        &field_ind,
2344                    )?;
2345                    if inner_field.optional {
2346                        writeln!(output, "{}}}", ind)?;
2347                    }
2348                }
2349                writeln!(output)?;
2350
2351                writeln!(
2352                    output,
2353                    "{}err = synta_encoder_end_constructed(nested_encoder);",
2354                    ind
2355                )?;
2356                writeln!(
2357                    output,
2358                    "{}if (err != SyntaErrorCode_Success) return err;",
2359                    ind
2360                )?;
2361            }
2362            Type::SequenceOf(inner_ty, _) | Type::SetOf(inner_ty, _) => {
2363                let is_sequence = matches!(ty, Type::SequenceOf(_, _));
2364                let container_type = if is_sequence { "sequence" } else { "set" };
2365
2366                writeln!(output, "{}// Encode SEQUENCE OF / SET OF", ind)?;
2367                writeln!(output, "{}SyntaEncoder* array_encoder = NULL;", ind)?;
2368                writeln!(
2369                    output,
2370                    "{}err = synta_encoder_start_{}({}, &array_encoder);",
2371                    ind, container_type, encoder_name
2372                )?;
2373                writeln!(
2374                    output,
2375                    "{}if (err != SyntaErrorCode_Success) return err;",
2376                    ind
2377                )?;
2378                writeln!(output)?;
2379
2380                writeln!(output, "{}// Encode each element", ind)?;
2381                writeln!(
2382                    output,
2383                    "{}for (size_t i = 0; i < {}_count; i++) {{",
2384                    ind, var_name
2385                )?;
2386                generate_encode_element_field(
2387                    output,
2388                    inner_ty,
2389                    "array_encoder",
2390                    &format!("{}[i]", var_name),
2391                    "    ",
2392                )?;
2393                writeln!(output, "{}}}", ind)?;
2394                writeln!(output)?;
2395
2396                writeln!(
2397                    output,
2398                    "{}err = synta_encoder_end_constructed(array_encoder);",
2399                    ind
2400                )?;
2401                writeln!(
2402                    output,
2403                    "{}if (err != SyntaErrorCode_Success) return err;",
2404                    ind
2405                )?;
2406            }
2407            // make_encode_call returned None for a Tagged type: must be EXPLICIT
2408            // (Constrained is handled by the early return above).
2409            Type::Tagged {
2410                tag: tag_info,
2411                inner,
2412            } => match tag_info.tagging {
2413                Tagging::Explicit => {
2414                    let class = tag_class_c_str(&tag_info.class);
2415                    writeln!(
2416                        output,
2417                        "{}// Wrap in EXPLICIT [{cls} {num}] tag",
2418                        ind,
2419                        cls = class,
2420                        num = tag_info.number
2421                    )?;
2422                    writeln!(output, "{}{{", ind)?;
2423                    writeln!(output, "{}    SyntaEncoder* tagged_encoder = NULL;", ind)?;
2424                    writeln!(output, "{}    SyntaTag explicit_tag;", ind)?;
2425                    writeln!(output, "{}    explicit_tag.class_ = {};", ind, class)?;
2426                    writeln!(output, "{}    explicit_tag.constructed = true;", ind)?;
2427                    writeln!(
2428                        output,
2429                        "{}    explicit_tag.number = {};",
2430                        ind, tag_info.number
2431                    )?;
2432                    writeln!(
2433                        output,
2434                        "{}    err = synta_encoder_start_constructed({}, explicit_tag, &tagged_encoder);",
2435                        ind, encoder_name
2436                    )?;
2437                    writeln!(
2438                        output,
2439                        "{}    if (err != SyntaErrorCode_Success) return err;",
2440                        ind
2441                    )?;
2442                    if let Some(call) = make_encode_call(inner, "tagged_encoder", var_name) {
2443                        writeln!(output, "{}    err = {};", ind, call)?;
2444                        writeln!(
2445                            output,
2446                            "{}    if (err != SyntaErrorCode_Success) return err;",
2447                            ind
2448                        )?;
2449                    } else {
2450                        writeln!(output, "{}    /* structural inner type inside EXPLICIT tag: not yet supported */", ind)?;
2451                        writeln!(output, "{}    return SyntaErrorCode_InvalidEncoding;", ind)?;
2452                    }
2453                    writeln!(
2454                        output,
2455                        "{}    err = synta_encoder_end_constructed(tagged_encoder);",
2456                        ind
2457                    )?;
2458                    writeln!(
2459                        output,
2460                        "{}    if (err != SyntaErrorCode_Success) return err;",
2461                        ind
2462                    )?;
2463                    writeln!(output, "{}}}", ind)?;
2464                }
2465                Tagging::Implicit => {
2466                    // Structural IMPLICIT types are handled by the early-return block above;
2467                    // this arm handles the primitive case (make_encode_call returned Some).
2468                    generate_field_encode(output, inner, var_name, encoder_name, indent, ctx)?;
2469                }
2470            },
2471            _ => {
2472                writeln!(
2473                    output,
2474                    "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported field type */",
2475                    ind
2476                )?;
2477                writeln!(
2478                    output,
2479                    "{}if (err != SyntaErrorCode_Success) return err;",
2480                    ind
2481                )?;
2482            }
2483        }
2484    } // closes else
2485
2486    Ok(())
2487}
2488
2489/// Emit a decode-side SIZE constraint check for a SEQUENCE OF / SET OF count.
2490///
2491/// Generates C code that verifies `count_var` (a `size_t`) against the
2492/// `bounds` constraint *after* all elements have been decoded.  On failure,
2493/// `array` and `array_decoder` are freed and `SyntaErrorCode_InvalidEncoding`
2494/// is returned.
2495fn emit_array_size_check(
2496    output: &mut String,
2497    bounds: ArraySizeBounds,
2498    count_var: &str,
2499) -> Result<(), Box<dyn std::error::Error>> {
2500    let (min, max) = bounds;
2501    let mut conditions: Vec<String> = Vec::new();
2502    if let Some(n) = min {
2503        if n > 0 {
2504            conditions.push(format!("{} < (size_t){}ULL", count_var, n));
2505        }
2506        // min == 0: always satisfied for size_t
2507    }
2508    if let Some(n) = max {
2509        conditions.push(format!("{} > (size_t){}ULL", count_var, n));
2510    }
2511    if !conditions.is_empty() {
2512        writeln!(output, "    // Validate SIZE constraint on collection")?;
2513        writeln!(output, "    if ({}) {{", conditions.join(" || "))?;
2514        writeln!(output, "        free(array);")?;
2515        writeln!(output, "        synta_decoder_free(array_decoder);")?;
2516        writeln!(output, "        return SyntaErrorCode_InvalidEncoding;")?;
2517        writeln!(output, "    }}")?;
2518    }
2519    Ok(())
2520}
2521
2522/// Emit an encode-side SIZE constraint check for a SEQUENCE OF / SET OF count.
2523///
2524/// Generates C code that verifies `count_var` (a `size_t` expression) against
2525/// the `bounds` constraint *before* encoding any elements.  On failure,
2526/// `SyntaErrorCode_InvalidArgument` is returned immediately.
2527fn emit_array_size_check_encode(
2528    output: &mut String,
2529    bounds: ArraySizeBounds,
2530    count_var: &str,
2531) -> Result<(), Box<dyn std::error::Error>> {
2532    let (min, max) = bounds;
2533    let mut conditions: Vec<String> = Vec::new();
2534    if let Some(n) = min {
2535        if n > 0 {
2536            conditions.push(format!("{} < (size_t){}ULL", count_var, n));
2537        }
2538    }
2539    if let Some(n) = max {
2540        conditions.push(format!("{} > (size_t){}ULL", count_var, n));
2541    }
2542    if !conditions.is_empty() {
2543        writeln!(output, "    // Validate SIZE constraint before encoding")?;
2544        writeln!(output, "    if ({}) {{", conditions.join(" || "))?;
2545        writeln!(output, "        return SyntaErrorCode_InvalidArgument;")?;
2546        writeln!(output, "    }}")?;
2547    }
2548    Ok(())
2549}
2550
2551/// Emit a C permitted-alphabet (FROM) validation loop for a string field.
2552///
2553/// Iterates over the raw bytes of `var_name` (a `SyntaOctetString*`) and
2554/// returns `SyntaErrorCode_InvalidArgument` if any byte falls outside the
2555/// permitted character ranges.
2556///
2557/// For ASCII-range alphabets this is a simple byte comparison.  Multi-byte
2558/// UTF-8 code points are not supported: the generated loop compares raw
2559/// bytes, so callers must ensure the schema only contains ASCII ranges when
2560/// using this in C bindings.
2561fn emit_permitted_alphabet_validation(
2562    output: &mut String,
2563    ranges: &[CharRange],
2564    var_name: &str,
2565    ind: &str,
2566) -> Result<(), Box<dyn std::error::Error>> {
2567    let alpha_expr = generate_c_alphabet_expr(ranges);
2568    writeln!(
2569        output,
2570        "{}// Validate FROM (permitted alphabet) constraint",
2571        ind
2572    )?;
2573    writeln!(output, "{}{{", ind)?;
2574    writeln!(
2575        output,
2576        "{}    size_t _alen = synta_octet_string_len({});",
2577        ind, var_name
2578    )?;
2579    writeln!(
2580        output,
2581        "{}    const unsigned char* _ap = (const unsigned char*)synta_octet_string_data({});",
2582        ind, var_name
2583    )?;
2584    writeln!(output, "{}    size_t _ai;", ind)?;
2585    writeln!(output, "{}    for (_ai = 0; _ai < _alen; _ai++) {{", ind)?;
2586    writeln!(output, "{}        unsigned char _c = _ap[_ai];", ind)?;
2587    writeln!(
2588        output,
2589        "{}        if (!({alpha})) return SyntaErrorCode_InvalidArgument;",
2590        ind,
2591        alpha = alpha_expr
2592    )?;
2593    writeln!(output, "{}    }}", ind)?;
2594    writeln!(output, "{}}}", ind)?;
2595    Ok(())
2596}
2597
2598/// Emit C validation code for an ASN.1 constraint before an encode call.
2599///
2600/// Only emits code for constraint types that can be statically validated:
2601/// - INTEGER `ValueRange` and `SingleValue` constraints
2602/// - `SizeConstraint` (length bounds) on OCTET STRING and string types
2603/// - `PermittedAlphabet` (FROM) on string types
2604/// - `Intersection` of the above (e.g. `SIZE (1..64) FROM ('A'..'Z')`)
2605///
2606/// Unknown or unsupported constraints are silently skipped so the generated
2607/// code compiles without errors even when no validation is emitted.
2608fn emit_constraint_validation(
2609    output: &mut String,
2610    constraint: &Constraint,
2611    base_type: &Type,
2612    var_name: &str,
2613    ind: &str,
2614    mode: &PatternMode,
2615    with_containing: bool,
2616) -> Result<(), Box<dyn std::error::Error>> {
2617    if let ConstraintSpec::Subtype(ref spec) = constraint.spec {
2618        emit_subtype_constraint_validation(
2619            output,
2620            spec,
2621            base_type,
2622            var_name,
2623            ind,
2624            mode,
2625            with_containing,
2626        )?;
2627    }
2628    // GeneralConstraint (table constraints, user-defined): no validation generated
2629    Ok(())
2630}
2631
2632/// Emit C validation for a `SubtypeConstraint` element.
2633fn emit_subtype_constraint_validation(
2634    output: &mut String,
2635    spec: &SubtypeConstraint,
2636    base_type: &Type,
2637    var_name: &str,
2638    ind: &str,
2639    mode: &PatternMode,
2640    with_containing: bool,
2641) -> Result<(), Box<dyn std::error::Error>> {
2642    let actual_base = unwrap_type(base_type);
2643    match spec {
2644        SubtypeConstraint::ValueRange { min, max } => {
2645            if matches!(actual_base, Type::Integer(_, _)) {
2646                writeln!(output, "{}// Validate INTEGER range constraint", ind)?;
2647                writeln!(output, "{}{{", ind)?;
2648                writeln!(output, "{}    int64_t _constraint_val;", ind)?;
2649                writeln!(
2650                    output,
2651                    "{}    err = synta_integer_to_i64({}, &_constraint_val);",
2652                    ind, var_name
2653                )?;
2654                writeln!(
2655                    output,
2656                    "{}    if (err != SyntaErrorCode_Success) return err;",
2657                    ind
2658                )?;
2659                if let ConstraintValue::Integer(n) = min {
2660                    writeln!(
2661                        output,
2662                        "{}    if (_constraint_val < {}LL) return SyntaErrorCode_InvalidArgument;",
2663                        ind, n
2664                    )?;
2665                }
2666                if let ConstraintValue::Integer(n) = max {
2667                    writeln!(
2668                        output,
2669                        "{}    if (_constraint_val > {}LL) return SyntaErrorCode_InvalidArgument;",
2670                        ind, n
2671                    )?;
2672                }
2673                writeln!(output, "{}}}", ind)?;
2674            }
2675        }
2676        SubtypeConstraint::SingleValue(val) => {
2677            if matches!(actual_base, Type::Integer(_, _)) {
2678                if let ConstraintValue::Integer(n) = val {
2679                    writeln!(output, "{}// Validate INTEGER single-value constraint", ind)?;
2680                    writeln!(output, "{}{{", ind)?;
2681                    writeln!(output, "{}    int64_t _constraint_val;", ind)?;
2682                    writeln!(
2683                        output,
2684                        "{}    err = synta_integer_to_i64({}, &_constraint_val);",
2685                        ind, var_name
2686                    )?;
2687                    writeln!(
2688                        output,
2689                        "{}    if (err != SyntaErrorCode_Success) return err;",
2690                        ind
2691                    )?;
2692                    writeln!(
2693                        output,
2694                        "{}    if (_constraint_val != {}LL) return SyntaErrorCode_InvalidArgument;",
2695                        ind, n
2696                    )?;
2697                    writeln!(output, "{}}}", ind)?;
2698                }
2699            }
2700        }
2701        SubtypeConstraint::SizeConstraint(inner_spec) => match actual_base {
2702            Type::OctetString(_)
2703            | Type::Utf8String(_)
2704            | Type::PrintableString(_)
2705            | Type::IA5String(_)
2706            | Type::Any
2707            | Type::AnyDefinedBy(_) => {
2708                emit_size_constraint_validation(output, inner_spec, var_name, ind)?;
2709            }
2710            _ => {}
2711        },
2712        SubtypeConstraint::PermittedAlphabet(ranges) => {
2713            if matches!(
2714                actual_base,
2715                Type::OctetString(_)
2716                    | Type::Utf8String(_)
2717                    | Type::PrintableString(_)
2718                    | Type::IA5String(_)
2719            ) {
2720                emit_permitted_alphabet_validation(output, ranges, var_name, ind)?;
2721            }
2722        }
2723        SubtypeConstraint::Intersection(parts) => {
2724            // Recurse over each part so combined constraints like
2725            // `SIZE (1..64) FROM ('A'..'Z')` each get their own check.
2726            for part in parts {
2727                emit_subtype_constraint_validation(
2728                    output,
2729                    part,
2730                    base_type,
2731                    var_name,
2732                    ind,
2733                    mode,
2734                    with_containing,
2735                )?;
2736            }
2737        }
2738        SubtypeConstraint::Union(_) => {
2739            writeln!(
2740                output,
2741                "{}/* UNION constraint: validation not generated */",
2742                ind
2743            )?;
2744        }
2745        SubtypeConstraint::ContainedSubtype(inner_ty) => {
2746            let is_byte_string = matches!(
2747                actual_base,
2748                Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_)
2749            );
2750            if is_byte_string {
2751                if with_containing {
2752                    emit_containing_validation(output, inner_ty, var_name, ind)?;
2753                } else {
2754                    writeln!(
2755                        output,
2756                        "{}/* CONTAINING constraint skipped; use --with-containing to enable validation */",
2757                        ind
2758                    )?;
2759                }
2760            }
2761        }
2762        SubtypeConstraint::Pattern(p) => {
2763            let is_string = matches!(
2764                unwrap_type(base_type),
2765                Type::OctetString(_)
2766                    | Type::Utf8String(_)
2767                    | Type::PrintableString(_)
2768                    | Type::IA5String(_)
2769            );
2770            if is_string {
2771                match mode {
2772                    PatternMode::Skip => {
2773                        writeln!(
2774                            output,
2775                            "{}/* PATTERN constraint \"{}\" skipped; compile with --with-regex for validation */",
2776                            ind, p
2777                        )?;
2778                    }
2779                    PatternMode::Posix => {
2780                        emit_posix_pattern_validation(output, p, var_name, ind)?;
2781                    }
2782                    PatternMode::Pcre2 => {
2783                        emit_pcre2_pattern_validation(output, p, var_name, ind)?;
2784                    }
2785                }
2786            }
2787        }
2788        // InnerType, Complement: validation not generated
2789        _ => {}
2790    }
2791    Ok(())
2792}
2793
2794/// Emit a POSIX ERE validation block for a PATTERN constraint.
2795///
2796/// Uses `regcomp` / `regexec` / `regfree` from `<regex.h>`.  Because
2797/// `regexec` requires a null-terminated string, a temporary heap allocation
2798/// is made for the string value.  The regex is compiled on every call;
2799/// for hot paths a static + `pthread_once` optimisation can be applied
2800/// manually after code generation.
2801fn emit_posix_pattern_validation(
2802    output: &mut String,
2803    pattern: &str,
2804    var_name: &str,
2805    ind: &str,
2806) -> Result<(), Box<dyn std::error::Error>> {
2807    // Escape backslashes and double-quotes for embedding in a C string literal.
2808    let escaped = pattern.replace('\\', "\\\\").replace('"', "\\\"");
2809    writeln!(
2810        output,
2811        "{}// Validate PATTERN constraint \"{}\" (POSIX ERE)",
2812        ind, pattern
2813    )?;
2814    writeln!(output, "{}{{", ind)?;
2815    writeln!(
2816        output,
2817        "{}    size_t _plen = synta_octet_string_len({});",
2818        ind, var_name
2819    )?;
2820    writeln!(output, "{}    char* _pstr = (char*)malloc(_plen + 1);", ind)?;
2821    writeln!(
2822        output,
2823        "{}    if (_pstr == NULL) return SyntaErrorCode_OutOfMemory;",
2824        ind
2825    )?;
2826    writeln!(
2827        output,
2828        "{}    memcpy(_pstr, synta_octet_string_data({}), _plen);",
2829        ind, var_name
2830    )?;
2831    writeln!(output, "{}    _pstr[_plen] = '\\0';", ind)?;
2832    writeln!(output, "{}    regex_t _pre;", ind)?;
2833    writeln!(
2834        output,
2835        "{}    int _prc = regcomp(&_pre, \"{}\", REG_EXTENDED | REG_NOSUB);",
2836        ind, escaped
2837    )?;
2838    writeln!(output, "{}    if (_prc == 0) {{", ind)?;
2839    writeln!(
2840        output,
2841        "{}        _prc = regexec(&_pre, _pstr, 0, NULL, 0);",
2842        ind
2843    )?;
2844    writeln!(output, "{}        regfree(&_pre);", ind)?;
2845    writeln!(output, "{}    }}", ind)?;
2846    writeln!(output, "{}    free(_pstr);", ind)?;
2847    writeln!(
2848        output,
2849        "{}    if (_prc != 0) return SyntaErrorCode_InvalidArgument;",
2850        ind
2851    )?;
2852    writeln!(output, "{}}}", ind)?;
2853    Ok(())
2854}
2855
2856/// Emit a PCRE2 validation block for a PATTERN constraint.
2857///
2858/// Uses `pcre2_compile` / `pcre2_match` / `pcre2_code_free` from `<pcre2.h>`
2859/// (8-bit code-unit width, `PCRE2_CODE_UNIT_WIDTH 8`).  Unlike the POSIX
2860/// path, no null-termination is required; the raw `SyntaOctetString` data
2861/// is passed directly with its length.
2862fn emit_pcre2_pattern_validation(
2863    output: &mut String,
2864    pattern: &str,
2865    var_name: &str,
2866    ind: &str,
2867) -> Result<(), Box<dyn std::error::Error>> {
2868    let escaped = pattern.replace('\\', "\\\\").replace('"', "\\\"");
2869    writeln!(
2870        output,
2871        "{}// Validate PATTERN constraint \"{}\" (PCRE2)",
2872        ind, pattern
2873    )?;
2874    writeln!(output, "{}{{", ind)?;
2875    writeln!(output, "{}    int _perr;", ind)?;
2876    writeln!(output, "{}    PCRE2_SIZE _perr_ofs;", ind)?;
2877    writeln!(
2878        output,
2879        "{}    size_t _plen = synta_octet_string_len({});",
2880        ind, var_name
2881    )?;
2882    writeln!(
2883        output,
2884        "{}    const PCRE2_UCHAR8* _pdata = (const PCRE2_UCHAR8*)synta_octet_string_data({});",
2885        ind, var_name
2886    )?;
2887    writeln!(
2888        output,
2889        "{}    pcre2_code* _pcode = pcre2_compile((PCRE2_SPTR8)\"{}\", PCRE2_ZERO_TERMINATED, 0, &_perr, &_perr_ofs, NULL);",
2890        ind, escaped
2891    )?;
2892    writeln!(output, "{}    if (_pcode != NULL) {{", ind)?;
2893    writeln!(
2894        output,
2895        "{}        pcre2_match_data* _pmatch = pcre2_match_data_create_from_pattern(_pcode, NULL);",
2896        ind
2897    )?;
2898    writeln!(
2899        output,
2900        "{}        int _prc = (int)pcre2_match(_pcode, _pdata, _plen, 0, 0, _pmatch, NULL);",
2901        ind
2902    )?;
2903    writeln!(output, "{}        pcre2_match_data_free(_pmatch);", ind)?;
2904    writeln!(output, "{}        pcre2_code_free(_pcode);", ind)?;
2905    writeln!(
2906        output,
2907        "{}        if (_prc < 0) return SyntaErrorCode_InvalidArgument;",
2908        ind
2909    )?;
2910    writeln!(output, "{}    }}", ind)?;
2911    writeln!(output, "{}}}", ind)?;
2912    Ok(())
2913}
2914
2915/// Emit a CONTAINING constraint validation block.
2916///
2917/// Creates a scratch `SyntaDecoder` over the field's raw bytes and calls the
2918/// inner type's generated `_decode()` function.  If decoding fails the
2919/// encode function returns `SyntaErrorCode_InvalidArgument`.
2920///
2921/// Only `TypeRef` inner types are supported; for built-in types a comment is
2922/// emitted instead (they lack a generated `_decode` symbol to call).
2923fn emit_containing_validation(
2924    output: &mut String,
2925    inner_type: &Type,
2926    var_name: &str,
2927    ind: &str,
2928) -> Result<(), Box<dyn std::error::Error>> {
2929    // Resolve the inner type name.  Only TypeRef is directly supported.
2930    let (inner_c_type, inner_fn) = match inner_type {
2931        Type::TypeRef(name) => (to_pascal_case(name), to_snake_case(name)),
2932        _ => {
2933            writeln!(
2934                output,
2935                "{}/* CONTAINING (built-in type): validation not generated */",
2936                ind
2937            )?;
2938            return Ok(());
2939        }
2940    };
2941    writeln!(
2942        output,
2943        "{}// Validate CONTAINING constraint ({})",
2944        ind, inner_c_type
2945    )?;
2946    writeln!(output, "{}{{", ind)?;
2947    writeln!(
2948        output,
2949        "{}    SyntaDecoder* _inner_dec = synta_decoder_new(",
2950        ind
2951    )?;
2952    writeln!(
2953        output,
2954        "{}        synta_octet_string_data({}),",
2955        ind, var_name
2956    )?;
2957    writeln!(
2958        output,
2959        "{}        (uint32_t)synta_octet_string_len({}),",
2960        ind, var_name
2961    )?;
2962    writeln!(output, "{}        SyntaEncoding_Der);", ind)?;
2963    writeln!(
2964        output,
2965        "{}    if (_inner_dec == NULL) return SyntaErrorCode_OutOfMemory;",
2966        ind
2967    )?;
2968    writeln!(output, "{}    {} _inner_val;", ind, inner_c_type)?;
2969    writeln!(
2970        output,
2971        "{}    SyntaErrorCode _inner_err = {}_decode(_inner_dec, &_inner_val);",
2972        ind, inner_fn
2973    )?;
2974    writeln!(output, "{}    synta_decoder_free(_inner_dec);", ind)?;
2975    writeln!(
2976        output,
2977        "{}    if (_inner_err != SyntaErrorCode_Success) return SyntaErrorCode_InvalidArgument;",
2978        ind
2979    )?;
2980    writeln!(output, "{}    {}_free(&_inner_val);", ind, inner_fn)?;
2981    writeln!(output, "{}}}", ind)?;
2982    Ok(())
2983}
2984
2985/// Emit C validation for a SIZE constraint on string / OCTET STRING fields.
2986///
2987/// The `spec` is the constraint nested inside `SubtypeConstraint::SizeConstraint`
2988/// and describes the allowed length range (a `ValueRange` or `SingleValue`).
2989fn emit_size_constraint_validation(
2990    output: &mut String,
2991    spec: &SubtypeConstraint,
2992    var_name: &str,
2993    ind: &str,
2994) -> Result<(), Box<dyn std::error::Error>> {
2995    match spec {
2996        SubtypeConstraint::ValueRange { min, max } => {
2997            writeln!(output, "{}// Validate SIZE range constraint", ind)?;
2998            writeln!(output, "{}{{", ind)?;
2999            writeln!(
3000                output,
3001                "{}    size_t _len = synta_octet_string_len({});",
3002                ind, var_name
3003            )?;
3004            if let ConstraintValue::Integer(n) = min {
3005                writeln!(
3006                    output,
3007                    "{}    if (_len < (size_t){}UL) return SyntaErrorCode_InvalidArgument;",
3008                    ind, n
3009                )?;
3010            }
3011            if let ConstraintValue::Integer(n) = max {
3012                writeln!(
3013                    output,
3014                    "{}    if (_len > (size_t){}UL) return SyntaErrorCode_MaxLengthExceeded;",
3015                    ind, n
3016                )?;
3017            }
3018            writeln!(output, "{}}}", ind)?;
3019        }
3020        SubtypeConstraint::SingleValue(val) => {
3021            if let ConstraintValue::Integer(n) = val {
3022                writeln!(output, "{}// Validate fixed SIZE constraint", ind)?;
3023                writeln!(output, "{}{{", ind)?;
3024                writeln!(
3025                    output,
3026                    "{}    size_t _len = synta_octet_string_len({});",
3027                    ind, var_name
3028                )?;
3029                writeln!(
3030                    output,
3031                    "{}    if (_len != (size_t){}UL) return SyntaErrorCode_InvalidArgument;",
3032                    ind, n
3033                )?;
3034                writeln!(output, "{}}}", ind)?;
3035            }
3036        }
3037        _ => {
3038            writeln!(
3039                output,
3040                "{}/* complex SIZE constraint: validation not generated */",
3041                ind
3042            )?;
3043        }
3044    }
3045    Ok(())
3046}
3047
3048/// Map an ASN.1 tag class to the corresponding C `SyntaTagClass_*` constant name.
3049fn tag_class_c_str(class: &TagClass) -> &'static str {
3050    match class {
3051        TagClass::Universal => "SyntaTagClass_Universal",
3052        TagClass::Application => "SyntaTagClass_Application",
3053        TagClass::ContextSpecific => "SyntaTagClass_ContextSpecific",
3054        TagClass::Private => "SyntaTagClass_Private",
3055    }
3056}
3057
3058/// Resolve a `TypeRef` name to its underlying `Type` definition (one level only).
3059fn resolve_typeref_once<'a>(name: &str, defs: &'a [Definition]) -> Option<&'a Type> {
3060    defs.iter()
3061        .find(|d| d.name.eq_ignore_ascii_case(name))
3062        .map(|d| &d.ty)
3063}
3064
3065/// Follow TypeRef aliases to find the underlying concrete type.
3066/// Stops when the type is not a TypeRef or when it cannot be resolved.
3067fn resolve_to_base<'a>(ty: &'a Type, defs: &'a [Definition]) -> &'a Type {
3068    match ty {
3069        Type::TypeRef(name) => {
3070            if let Some(resolved) = resolve_typeref_once(name, defs) {
3071                resolve_to_base(resolved, defs)
3072            } else {
3073                ty
3074            }
3075        }
3076        Type::Tagged { inner, .. }
3077        | Type::Constrained {
3078            base_type: inner, ..
3079        } => resolve_to_base(inner, defs),
3080        other => other,
3081    }
3082}
3083
3084/// Get the tag checking condition for a given type
3085fn get_tag_check_condition(ty: &Type, defs: &[Definition]) -> String {
3086    match ty {
3087        Type::Boolean => {
3088            "tag.class_ == SyntaTagClass_Universal && tag.number == 1 && !tag.constructed"
3089                .to_string()
3090        }
3091        Type::Integer(_, _) => {
3092            "tag.class_ == SyntaTagClass_Universal && tag.number == 2 && !tag.constructed"
3093                .to_string()
3094        }
3095        Type::BitString(_) => {
3096            "tag.class_ == SyntaTagClass_Universal && tag.number == 3 && !tag.constructed"
3097                .to_string()
3098        }
3099        Type::OctetString(_) => {
3100            "tag.class_ == SyntaTagClass_Universal && tag.number == 4 && !tag.constructed"
3101                .to_string()
3102        }
3103        Type::Null => {
3104            "tag.class_ == SyntaTagClass_Universal && tag.number == 5 && !tag.constructed"
3105                .to_string()
3106        }
3107        Type::ObjectIdentifier => {
3108            "tag.class_ == SyntaTagClass_Universal && tag.number == 6 && !tag.constructed"
3109                .to_string()
3110        }
3111        Type::Real => {
3112            "tag.class_ == SyntaTagClass_Universal && tag.number == 9 && !tag.constructed"
3113                .to_string()
3114        }
3115        Type::Enumerated(_) => {
3116            "tag.class_ == SyntaTagClass_Universal && tag.number == 10 && !tag.constructed"
3117                .to_string()
3118        }
3119        Type::Utf8String(_) => {
3120            "tag.class_ == SyntaTagClass_Universal && tag.number == 12 && !tag.constructed"
3121                .to_string()
3122        }
3123        Type::PrintableString(_) => {
3124            "tag.class_ == SyntaTagClass_Universal && tag.number == 19 && !tag.constructed"
3125                .to_string()
3126        }
3127        Type::IA5String(_) => {
3128            "tag.class_ == SyntaTagClass_Universal && tag.number == 22 && !tag.constructed"
3129                .to_string()
3130        }
3131        Type::Sequence(_) => {
3132            "tag.class_ == SyntaTagClass_Universal && tag.number == 16 && tag.constructed"
3133                .to_string()
3134        }
3135        Type::SequenceOf(_, _) => {
3136            "tag.class_ == SyntaTagClass_Universal && tag.number == 16 && tag.constructed"
3137                .to_string()
3138        }
3139        Type::Set(_) => {
3140            "tag.class_ == SyntaTagClass_Universal && tag.number == 17 && tag.constructed"
3141                .to_string()
3142        }
3143        Type::SetOf(_, _) => {
3144            "tag.class_ == SyntaTagClass_Universal && tag.number == 17 && tag.constructed"
3145                .to_string()
3146        }
3147        Type::UtcTime => {
3148            "tag.class_ == SyntaTagClass_Universal && tag.number == 23 && !tag.constructed"
3149                .to_string()
3150        }
3151        Type::GeneralizedTime => {
3152            "tag.class_ == SyntaTagClass_Universal && tag.number == 24 && !tag.constructed"
3153                .to_string()
3154        }
3155        Type::Tagged {
3156            tag: tag_info,
3157            inner,
3158        } => {
3159            let class = tag_class_c_str(&tag_info.class);
3160            // EXPLICIT tagging always produces a constructed outer TLV.
3161            // IMPLICIT tagging inherits the constructed bit of the inner type:
3162            //   constructed for SEQUENCE, SET, SEQUENCE OF, SET OF, CHOICE;
3163            //   primitive otherwise.
3164            let constructed = match tag_info.tagging {
3165                crate::ast::Tagging::Explicit => "tag.constructed",
3166                crate::ast::Tagging::Implicit => {
3167                    let concrete = resolve_to_base(inner, defs);
3168                    if matches!(
3169                        concrete,
3170                        Type::Sequence(_)
3171                            | Type::Set(_)
3172                            | Type::SequenceOf(_, _)
3173                            | Type::SetOf(_, _)
3174                            | Type::Choice(_)
3175                    ) {
3176                        "tag.constructed"
3177                    } else {
3178                        "!tag.constructed"
3179                    }
3180                }
3181            };
3182            format!(
3183                "tag.class_ == {} && tag.number == {} && {}",
3184                class, tag_info.number, constructed
3185            )
3186        }
3187        Type::TypeRef(name) => {
3188            // Resolve the TypeRef to its underlying type and get the tag check.
3189            // Walk at most two levels to handle chained aliases.
3190            if let Some(underlying) = resolve_typeref_once(name, defs) {
3191                if let Type::TypeRef(_) = underlying {
3192                    get_tag_check_condition(underlying, defs)
3193                } else {
3194                    get_tag_check_condition(underlying, defs)
3195                }
3196            } else {
3197                "true /* TypeRef - unresolved, match any tag */".to_string()
3198            }
3199        }
3200        Type::Any | Type::AnyDefinedBy(_) => {
3201            // ANY has no fixed tag — accept whatever tag is present
3202            "true /* ANY - match any tag */".to_string()
3203        }
3204        _ => {
3205            // For other types, we can't determine the tag
3206            "false /* Unknown type tag */".to_string()
3207        }
3208    }
3209}
3210
3211/// Generate code to decode a choice variant
3212fn generate_choice_variant_decode(
3213    output: &mut String,
3214    ty: &Type,
3215    var_name: &str,
3216) -> Result<(), Box<dyn std::error::Error>> {
3217    if let Some(call) = make_decode_call(ty, "decoder", var_name) {
3218        writeln!(output, "        err = {};", call)?;
3219        writeln!(
3220            output,
3221            "        if (err != SyntaErrorCode_Success) return err;"
3222        )?;
3223    } else {
3224        match ty {
3225            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
3226                let is_sequence = matches!(ty, Type::Sequence(_));
3227                let container_type = if is_sequence { "sequence" } else { "set" };
3228                writeln!(
3229                    output,
3230                    "        /* Decode nested {} choice variant */",
3231                    container_type.to_uppercase()
3232                )?;
3233                writeln!(output, "        SyntaDecoder* nested_decoder = NULL;")?;
3234                writeln!(
3235                    output,
3236                    "        err = synta_decoder_enter_{}(decoder, &nested_decoder);",
3237                    container_type
3238                )?;
3239                writeln!(
3240                    output,
3241                    "        if (err != SyntaErrorCode_Success) return err;"
3242                )?;
3243                writeln!(output)?;
3244                for inner_field in inner_fields {
3245                    if matches!(inner_field.ty, Type::Null) {
3246                        continue;
3247                    }
3248                    let inner_name = to_snake_case(&inner_field.name);
3249                    let nested_var = format!("{}.{}", var_name, inner_name);
3250                    generate_choice_seq_field_decode(
3251                        output,
3252                        &inner_field.ty,
3253                        &nested_var,
3254                        "        ",
3255                    )?;
3256                    writeln!(output)?;
3257                }
3258                writeln!(output, "        synta_decoder_free(nested_decoder);")?;
3259            }
3260            _ => {
3261                writeln!(output, "        return SyntaErrorCode_InvalidEncoding;")?;
3262            }
3263        }
3264    } // closes else
3265    Ok(())
3266}
3267
3268/// Generate code to encode a choice variant
3269fn generate_choice_variant_encode(
3270    output: &mut String,
3271    ty: &Type,
3272    var_name: &str,
3273) -> Result<(), Box<dyn std::error::Error>> {
3274    if let Some(call) = make_encode_call(ty, "encoder", var_name) {
3275        writeln!(output, "            err = {};", call)?;
3276        writeln!(
3277            output,
3278            "            if (err != SyntaErrorCode_Success) return err;"
3279        )?;
3280    } else {
3281        match ty {
3282            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
3283                let is_sequence = matches!(ty, Type::Sequence(_));
3284                let container_type = if is_sequence { "sequence" } else { "set" };
3285                writeln!(
3286                    output,
3287                    "            /* Encode nested {} choice variant */",
3288                    container_type.to_uppercase()
3289                )?;
3290                writeln!(output, "            SyntaEncoder* nested_encoder = NULL;")?;
3291                writeln!(
3292                    output,
3293                    "            err = synta_encoder_start_{}(encoder, &nested_encoder);",
3294                    container_type
3295                )?;
3296                writeln!(
3297                    output,
3298                    "            if (err != SyntaErrorCode_Success) return err;"
3299                )?;
3300                writeln!(output)?;
3301                for inner_field in inner_fields {
3302                    if matches!(inner_field.ty, Type::Null) {
3303                        continue;
3304                    }
3305                    let inner_name = to_snake_case(&inner_field.name);
3306                    let nested_var = format!("{}.{}", var_name, inner_name);
3307                    generate_nested_inline_field_encode(
3308                        output,
3309                        &inner_field.ty,
3310                        &nested_var,
3311                        "            ",
3312                    )?;
3313                }
3314                writeln!(output)?;
3315                writeln!(
3316                    output,
3317                    "            err = synta_encoder_end_constructed(nested_encoder);"
3318                )?;
3319                writeln!(
3320                    output,
3321                    "            if (err != SyntaErrorCode_Success) return err;"
3322                )?;
3323            }
3324            _ => {
3325                writeln!(output, "            return SyntaErrorCode_InvalidEncoding;")?;
3326            }
3327        }
3328    } // closes else
3329    Ok(())
3330}
3331
3332/// Generate code to free a choice variant
3333fn generate_choice_variant_free(
3334    output: &mut String,
3335    ty: &Type,
3336    var_name: &str,
3337    defs: &[Definition],
3338) -> Result<(), Box<dyn std::error::Error>> {
3339    match ty {
3340        Type::Integer(_, _) => {
3341            writeln!(output, "            synta_integer_free({});", var_name)?;
3342        }
3343        Type::Enumerated(_) => {
3344            // C enum stored by value — no heap allocation, nothing to free
3345        }
3346        Type::ObjectIdentifier => {
3347            writeln!(output, "            synta_oid_free({});", var_name)?;
3348        }
3349        Type::OctetString(_)
3350        | Type::Utf8String(_)
3351        | Type::PrintableString(_)
3352        | Type::IA5String(_)
3353        | Type::UtcTime
3354        | Type::GeneralizedTime
3355        | Type::Any
3356        | Type::AnyDefinedBy(_) => {
3357            writeln!(output, "            synta_octet_string_free({});", var_name)?;
3358        }
3359        Type::BitString(_) => {
3360            writeln!(
3361                output,
3362                "            synta_byte_array_free(&{}.data);",
3363                var_name
3364            )?;
3365        }
3366        Type::TypeRef(type_name) => {
3367            let name = type_name.clone();
3368            emit_typeref_free(output, &name, var_name, "            ", defs)?;
3369        }
3370        Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
3371            for inner_field in inner_fields {
3372                if !needs_freeing(&inner_field.ty, defs) {
3373                    continue;
3374                }
3375                let inner_name = to_snake_case(&inner_field.name);
3376                let nested_var = format!("{}.{}", var_name, inner_name);
3377                if inner_field.optional {
3378                    writeln!(
3379                        output,
3380                        "            if ({}.has_{}) {{",
3381                        var_name, inner_name
3382                    )?;
3383                    generate_free_stmt(output, &inner_field.ty, &nested_var, "                ")?;
3384                    writeln!(output, "            }}")?;
3385                } else {
3386                    generate_free_stmt(output, &inner_field.ty, &nested_var, "            ")?;
3387                }
3388            }
3389        }
3390        _ => {
3391            // No freeing needed for this type
3392        }
3393    }
3394    Ok(())
3395}
3396
3397/// Emit the correct free statement for a `TypeRef(type_name)` field.
3398///
3399/// Looks up `type_name` in `defs` and emits whichever primitive free call
3400/// (or `{type_fn}_free`) is appropriate.  For value types that need no heap
3401/// cleanup (ENUMERATED, named INTEGER, BOOLEAN, …) nothing is emitted.
3402fn emit_typeref_free(
3403    output: &mut String,
3404    type_name: &str,
3405    field_access: &str,
3406    ind: &str,
3407    defs: &[Definition],
3408) -> Result<(), Box<dyn std::error::Error>> {
3409    if let Some(def) = defs.iter().find(|d| d.name == type_name) {
3410        match &def.ty {
3411            // Complex types that have a generated _free function
3412            Type::Sequence(_)
3413            | Type::Set(_)
3414            | Type::Choice(_)
3415            | Type::SequenceOf(_, _)
3416            | Type::SetOf(_, _) => {
3417                let type_fn = to_snake_case(type_name);
3418                writeln!(output, "{}{}_free(&{});", ind, type_fn, field_access)?;
3419            }
3420            // Constrained string/bit newtypes have a generated static-inline _free
3421            Type::Constrained { base_type, .. }
3422                if matches!(
3423                    base_type.as_ref(),
3424                    Type::IA5String(_)
3425                        | Type::PrintableString(_)
3426                        | Type::Utf8String(_)
3427                        | Type::OctetString(_)
3428                        | Type::BitString(_)
3429                ) =>
3430            {
3431                let type_fn = to_snake_case(type_name);
3432                writeln!(output, "{}{}_free(&{});", ind, type_fn, field_access)?;
3433            }
3434            // Named integer (typedef int64_t) — no allocation, no free
3435            Type::Integer(_, named) if !named.is_empty() => {}
3436            // Regular integer — SyntaInteger*
3437            Type::Integer(_, _) => {
3438                writeln!(output, "{}synta_integer_free({});", ind, field_access)?;
3439            }
3440            // ENUMERATED — C enum stored by value, no allocation
3441            Type::Enumerated(_) => {}
3442            // OID alias
3443            Type::ObjectIdentifier => {
3444                writeln!(output, "{}synta_oid_free({});", ind, field_access)?;
3445            }
3446            // Unconstrained string / time / ANY aliases — SyntaOctetString*
3447            Type::OctetString(_)
3448            | Type::Utf8String(_)
3449            | Type::PrintableString(_)
3450            | Type::IA5String(_)
3451            | Type::UtcTime
3452            | Type::GeneralizedTime
3453            | Type::Any
3454            | Type::AnyDefinedBy(_) => {
3455                writeln!(output, "{}synta_octet_string_free({});", ind, field_access)?;
3456            }
3457            // BitString alias — by-value struct with embedded SyntaByteArray
3458            Type::BitString(_) => {
3459                writeln!(
3460                    output,
3461                    "{}synta_byte_array_free(&{}.data);",
3462                    ind, field_access
3463                )?;
3464            }
3465            // Chained TypeRef — recurse
3466            Type::TypeRef(inner_name) => {
3467                let inner = inner_name.clone();
3468                emit_typeref_free(output, &inner, field_access, ind, defs)?;
3469            }
3470            // Tagged wrapper — unwrap to find the real type
3471            Type::Tagged { inner, .. } => {
3472                let base = unwrap_type(inner);
3473                generate_free_stmt(output, base, field_access, ind)?;
3474            }
3475            // Boolean, Real, Null, other value types — no free needed
3476            _ => {}
3477        }
3478    } else {
3479        // Unknown definition (e.g. from another module) — assume _free exists
3480        let type_fn = to_snake_case(type_name);
3481        writeln!(output, "{}{}_free(&{});", ind, type_fn, field_access)?;
3482    }
3483    Ok(())
3484}
3485
3486/// Check if a type needs explicit freeing
3487fn needs_freeing(ty: &Type, defs: &[Definition]) -> bool {
3488    match ty {
3489        // Named integer is typedef int64_t — no heap allocation
3490        Type::Integer(_, named) if !named.is_empty() => false,
3491        // Regular integer is SyntaInteger* — needs freeing
3492        Type::Integer(_, _)
3493        | Type::ObjectIdentifier
3494        | Type::OctetString(_)
3495        | Type::Utf8String(_)
3496        | Type::PrintableString(_)
3497        | Type::IA5String(_)
3498        | Type::UtcTime
3499        | Type::GeneralizedTime
3500        | Type::Any
3501        | Type::AnyDefinedBy(_)
3502        | Type::BitString(_)
3503        | Type::Sequence(_)
3504        | Type::Set(_)
3505        | Type::Choice(_)
3506        | Type::SequenceOf(_, _)
3507        | Type::SetOf(_, _) => true,
3508        // ENUMERATED is a C enum stored by value — no heap allocation
3509        Type::Enumerated(_) => false,
3510        Type::TypeRef(name) => {
3511            // Resolve to underlying type and check recursively
3512            if let Some(def) = defs.iter().find(|d| &d.name == name) {
3513                needs_freeing(&def.ty, defs)
3514            } else {
3515                true // unknown type — assume it needs freeing
3516            }
3517        }
3518        Type::Boolean | Type::Real | Type::Null => false,
3519        _ => false,
3520    }
3521}
3522
3523/// Generate code to decode a single element at top level (for SEQUENCE OF / SET OF types)
3524fn generate_decode_element_toplevel(
3525    output: &mut String,
3526    ty: &Type,
3527    decoder_name: &str,
3528    var_name: &str,
3529) -> Result<(), Box<dyn std::error::Error>> {
3530    if let Some(call) = make_decode_call(ty, decoder_name, var_name) {
3531        writeln!(output, "        err = {};", call)?;
3532    } else {
3533        writeln!(output, "        err = SyntaErrorCode_InvalidEncoding;")?;
3534    }
3535    writeln!(output, "        if (err != SyntaErrorCode_Success) {{")?;
3536    writeln!(output, "            free(array);")?;
3537    writeln!(output, "            synta_decoder_free(array_decoder);")?;
3538    writeln!(output, "            return err;")?;
3539    writeln!(output, "        }}")?;
3540    Ok(())
3541}
3542
3543/// Generate code to encode a single element at top level
3544fn generate_encode_element_toplevel(
3545    output: &mut String,
3546    ty: &Type,
3547    encoder_name: &str,
3548    var_name: &str,
3549) -> Result<(), Box<dyn std::error::Error>> {
3550    if let Some(call) = make_encode_call(ty, encoder_name, var_name) {
3551        writeln!(output, "        err = {};", call)?;
3552    } else {
3553        writeln!(output, "        err = SyntaErrorCode_InvalidEncoding;")?;
3554    }
3555    writeln!(
3556        output,
3557        "        if (err != SyntaErrorCode_Success) return err;"
3558    )?;
3559    Ok(())
3560}
3561
3562/// Generate code to free a single element (for SEQUENCE OF / SET OF items).
3563fn generate_free_element(
3564    output: &mut String,
3565    ty: &Type,
3566    var_name: &str,
3567    defs: &[Definition],
3568) -> Result<(), Box<dyn std::error::Error>> {
3569    match ty {
3570        Type::Integer(_, _) => {
3571            writeln!(output, "        synta_integer_free({});", var_name)?;
3572        }
3573        Type::ObjectIdentifier => {
3574            writeln!(
3575                output,
3576                "        synta_object_identifier_free({});",
3577                var_name
3578            )?;
3579        }
3580        Type::OctetString(_) => {
3581            writeln!(output, "        synta_octet_string_free({});", var_name)?;
3582        }
3583        Type::BitString(_) => {
3584            writeln!(output, "        synta_byte_array_free(&{}.data);", var_name)?;
3585        }
3586        Type::TypeRef(type_name) => {
3587            let name = type_name.clone();
3588            emit_typeref_free(output, &name, var_name, "        ", defs)?;
3589        }
3590        _ => {}
3591    }
3592    Ok(())
3593}
3594
3595/// Generate code to encode array element within a field
3596fn generate_encode_element_field(
3597    output: &mut String,
3598    ty: &Type,
3599    encoder_name: &str,
3600    var_name: &str,
3601    ind: &str,
3602) -> Result<(), Box<dyn std::error::Error>> {
3603    if let Some(call) = make_encode_call(ty, encoder_name, var_name) {
3604        writeln!(output, "{}err = {};", ind, call)?;
3605    } else {
3606        writeln!(output, "{}err = SyntaErrorCode_InvalidEncoding;", ind)?;
3607    }
3608    writeln!(
3609        output,
3610        "{}if (err != SyntaErrorCode_Success) return err;",
3611        ind
3612    )?;
3613    Ok(())
3614}
3615
3616/// Generate code to decode a single element (for use in arrays)
3617fn generate_decode_element(
3618    output: &mut String,
3619    ty: &Type,
3620    decoder_name: &str,
3621    var_name: &str,
3622    ind: &str,
3623) -> Result<(), Box<dyn std::error::Error>> {
3624    if let Some(call) = make_decode_call(ty, decoder_name, var_name) {
3625        writeln!(output, "{}err = {};", ind, call)?;
3626    } else {
3627        writeln!(output, "{}return SyntaErrorCode_InvalidEncoding;", ind)?;
3628        return Ok(());
3629    }
3630    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3631    writeln!(output, "{}    free(array);", ind)?;
3632    writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
3633    writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
3634    writeln!(output, "{}    return err;", ind)?;
3635    writeln!(output, "{}}}", ind)?;
3636    Ok(())
3637}
3638
3639/// Generate decode code for one field of an inline nested SEQUENCE/SET.
3640/// Uses `nested_decoder`; frees both `nested_decoder` and `seq_decoder` on error.
3641fn generate_nested_inline_field_decode(
3642    output: &mut String,
3643    ty: &Type,
3644    var_name: &str,
3645    ind: &str,
3646) -> Result<(), Box<dyn std::error::Error>> {
3647    if let Some(call) = make_decode_call(ty, "nested_decoder", var_name) {
3648        writeln!(output, "{}err = {};", ind, call)?;
3649    } else {
3650        writeln!(
3651            output,
3652            "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported nested field type */",
3653            ind
3654        )?;
3655    }
3656    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3657    writeln!(output, "{}    synta_decoder_free(nested_decoder);", ind)?;
3658    writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
3659    writeln!(output, "{}    return err;", ind)?;
3660    writeln!(output, "{}}}", ind)?;
3661    Ok(())
3662}
3663
3664/// Generate decode code for one field inside a SEQUENCE/SET that is a choice variant.
3665///
3666/// Like `generate_nested_inline_field_decode` but only frees `nested_decoder`
3667/// on error — there is no outer `seq_decoder` in a choice variant context.
3668fn generate_choice_seq_field_decode(
3669    output: &mut String,
3670    ty: &Type,
3671    var_name: &str,
3672    ind: &str,
3673) -> Result<(), Box<dyn std::error::Error>> {
3674    if let Some(call) = make_decode_call(ty, "nested_decoder", var_name) {
3675        writeln!(output, "{}err = {};", ind, call)?;
3676    } else {
3677        writeln!(
3678            output,
3679            "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported nested field type */",
3680            ind
3681        )?;
3682    }
3683    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3684    writeln!(output, "{}    synta_decoder_free(nested_decoder);", ind)?;
3685    writeln!(output, "{}    return err;", ind)?;
3686    writeln!(output, "{}}}", ind)?;
3687    Ok(())
3688}
3689
3690/// Generate a free statement for a variable of the given type.
3691///
3692/// Does not handle optionality — the caller must emit a `has_` guard if needed.
3693fn generate_free_stmt(
3694    output: &mut String,
3695    ty: &Type,
3696    var_name: &str,
3697    ind: &str,
3698) -> Result<(), Box<dyn std::error::Error>> {
3699    match ty {
3700        Type::Integer(_, _) | Type::Enumerated(_) => {
3701            writeln!(output, "{}synta_integer_free({});", ind, var_name)?;
3702        }
3703        Type::ObjectIdentifier => {
3704            writeln!(output, "{}synta_oid_free({});", ind, var_name)?;
3705        }
3706        Type::OctetString(_)
3707        | Type::Utf8String(_)
3708        | Type::PrintableString(_)
3709        | Type::IA5String(_)
3710        | Type::UtcTime
3711        | Type::GeneralizedTime
3712        | Type::Any
3713        | Type::AnyDefinedBy(_) => {
3714            writeln!(output, "{}synta_octet_string_free({});", ind, var_name)?;
3715        }
3716        Type::BitString(_) => {
3717            writeln!(output, "{}synta_byte_array_free(&{}.data);", ind, var_name)?;
3718        }
3719        Type::TypeRef(type_name) => {
3720            let fn_name = to_snake_case(type_name);
3721            writeln!(output, "{}{}_free(&{});", ind, fn_name, var_name)?;
3722        }
3723        Type::Tagged { inner, .. }
3724        | Type::Constrained {
3725            base_type: inner, ..
3726        } => {
3727            generate_free_stmt(output, inner, var_name, ind)?;
3728        }
3729        _ => {} // Boolean, Real, Null, etc. — no heap allocation to free
3730    }
3731    Ok(())
3732}
3733
3734/// Generate encode code for one field of an inline nested SEQUENCE/SET.
3735/// Uses `nested_encoder`; returns on error.
3736fn generate_nested_inline_field_encode(
3737    output: &mut String,
3738    ty: &Type,
3739    var_name: &str,
3740    ind: &str,
3741) -> Result<(), Box<dyn std::error::Error>> {
3742    if let Some(call) = make_encode_call(ty, "nested_encoder", var_name) {
3743        writeln!(output, "{}err = {};", ind, call)?;
3744    } else {
3745        writeln!(
3746            output,
3747            "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported nested field type */",
3748            ind
3749        )?;
3750    }
3751    writeln!(
3752        output,
3753        "{}if (err != SyntaErrorCode_Success) return err;",
3754        ind
3755    )?;
3756    Ok(())
3757}
3758
3759// ─── Arena / bump-allocator variants ────────────────────────────────────────
3760
3761/// Dispatch to the appropriate arena decode generator for a top-level definition.
3762fn generate_type_arena_impl(
3763    output: &mut String,
3764    def: &Definition,
3765    defs: &[Definition],
3766) -> Result<(), Box<dyn std::error::Error>> {
3767    match &def.ty {
3768        Type::Sequence(fields) => {
3769            generate_sequence_arena_impl(output, &def.name, fields, defs, false)?;
3770        }
3771        Type::Set(fields) => {
3772            generate_sequence_arena_impl(output, &def.name, fields, defs, true)?;
3773        }
3774        Type::Choice(variants) => {
3775            generate_choice_arena_impl(output, &def.name, variants, defs)?;
3776        }
3777        Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
3778            generate_enum_arena_impl(output, &def.name)?;
3779        }
3780        Type::Enumerated(_) => {
3781            generate_enum_arena_impl(output, &def.name)?;
3782        }
3783        Type::SequenceOf(inner, opt_constraint) => {
3784            let size_c = opt_constraint.as_ref().map(legacy_size_to_bounds);
3785            generate_array_arena_impl(output, &def.name, inner, true, size_c)?;
3786        }
3787        Type::SetOf(inner, opt_constraint) => {
3788            let size_c = opt_constraint.as_ref().map(legacy_size_to_bounds);
3789            generate_array_arena_impl(output, &def.name, inner, false, size_c)?;
3790        }
3791        // Constrained SEQUENCE OF / SET OF: outer constraint holds the SIZE spec.
3792        Type::Constrained {
3793            base_type,
3794            constraint,
3795        } => {
3796            let size_c = modern_size_to_bounds(&constraint.spec);
3797            match base_type.as_ref() {
3798                Type::SequenceOf(inner, _) => {
3799                    generate_array_arena_impl(output, &def.name, inner, true, size_c)?;
3800                }
3801                Type::SetOf(inner, _) => {
3802                    generate_array_arena_impl(output, &def.name, inner, false, size_c)?;
3803                }
3804                _ => {
3805                    generate_simple_arena_impl(output, &def.name, &def.ty)?;
3806                }
3807            }
3808        }
3809        _ => {
3810            generate_simple_arena_impl(output, &def.name, &def.ty)?;
3811        }
3812    }
3813    Ok(())
3814}
3815
3816/// Like `make_decode_call` but uses `{name}_decode_arena` for TypeRef fields
3817/// so that sub-structure tracking is delegated to the referenced type's arena
3818/// decoder.  All scalar API calls (integer, octet-string, …) are unchanged.
3819fn make_decode_call_arena(ty: &Type, decoder: &str, arena: &str, var: &str) -> Option<String> {
3820    match ty {
3821        Type::Boolean => Some(format!("synta_decode_boolean({}, &{})", decoder, var)),
3822        Type::Integer(_, _) | Type::Enumerated(_) => {
3823            Some(format!("synta_decode_integer({}, &{})", decoder, var))
3824        }
3825        Type::Real => Some(format!("synta_decode_real({}, &{})", decoder, var)),
3826        Type::Null => Some(format!("synta_decode_null({})", decoder)),
3827        Type::ObjectIdentifier => Some(format!(
3828            "synta_decode_object_identifier({}, &{})",
3829            decoder, var
3830        )),
3831        Type::BitString(_) => Some(format!(
3832            "synta_decode_bit_string({}, &{}.data, &{}.unused_bits)",
3833            decoder, var, var
3834        )),
3835        Type::OctetString(_) | Type::Any | Type::AnyDefinedBy(_) => {
3836            Some(format!("synta_decode_octet_string({}, &{})", decoder, var))
3837        }
3838        Type::Utf8String(_) => Some(format!(
3839            "synta_decode_utf8_string_os({}, &{})",
3840            decoder, var
3841        )),
3842        Type::PrintableString(_) => Some(format!(
3843            "synta_decode_printable_string_os({}, &{})",
3844            decoder, var
3845        )),
3846        Type::IA5String(_) => Some(format!("synta_decode_ia5_string_os({}, &{})", decoder, var)),
3847        Type::UtcTime => Some(format!("synta_decode_utctime_os({}, &{})", decoder, var)),
3848        Type::GeneralizedTime => Some(format!(
3849            "synta_decode_generalized_time_os({}, &{})",
3850            decoder, var
3851        )),
3852        // TypeRef → call the arena variant so sub-field tracking is handled there.
3853        Type::TypeRef(name) => Some(format!(
3854            "{}_decode_arena({}, {}, &{})",
3855            to_snake_case(name),
3856            decoder,
3857            arena,
3858            var
3859        )),
3860        Type::Tagged { tag, inner } => match tag.tagging {
3861            Tagging::Explicit => None,
3862            Tagging::Implicit => make_decode_call_arena(inner, decoder, arena, var),
3863        },
3864        Type::Constrained {
3865            base_type: inner, ..
3866        } => make_decode_call_arena(inner, decoder, arena, var),
3867        _ => None,
3868    }
3869}
3870
3871/// Emit an `_synta_arena_track` call for `var_name` of type `ty` into `output`.
3872///
3873/// Only emits for types that produce heap-allocated handles (Integer, OctetString,
3874/// OID, BitString data).  For TypeRef the tracking is done inside the called
3875/// `_decode_arena` function, so nothing is emitted here.
3876fn emit_arena_track_field(
3877    output: &mut String,
3878    ty: &Type,
3879    var_name: &str,
3880    arena: &str,
3881    ind: &str,
3882) -> Result<(), Box<dyn std::error::Error>> {
3883    match unwrap_type(ty) {
3884        Type::Integer(_, _) | Type::Enumerated(_) => {
3885            writeln!(
3886                output,
3887                "{}_synta_arena_track({}, {}, (void(*)(void*))synta_integer_free);",
3888                ind, arena, var_name
3889            )?;
3890        }
3891        Type::OctetString(_)
3892        | Type::Utf8String(_)
3893        | Type::PrintableString(_)
3894        | Type::IA5String(_)
3895        | Type::UtcTime
3896        | Type::GeneralizedTime
3897        | Type::Any
3898        | Type::AnyDefinedBy(_) => {
3899            writeln!(
3900                output,
3901                "{}_synta_arena_track({}, {}, (void(*)(void*))synta_octet_string_free);",
3902                ind, arena, var_name
3903            )?;
3904        }
3905        Type::ObjectIdentifier => {
3906            writeln!(
3907                output,
3908                "{}_synta_arena_track({}, {}, (void(*)(void*))synta_oid_free);",
3909                ind, arena, var_name
3910            )?;
3911        }
3912        Type::BitString(_) => {
3913            writeln!(
3914                output,
3915                "{}if ({}.data.owned) _synta_arena_track({}, (void*){}.data.data, free);",
3916                ind, var_name, arena, var_name
3917            )?;
3918        }
3919        // TypeRef, Boolean, Real, Null, Sequence, SequenceOf, Choice, … — no tracking here.
3920        _ => {}
3921    }
3922    Ok(())
3923}
3924
3925/// Arena-aware version of `generate_nested_inline_field_decode`.
3926/// Uses `nested_decoder`; frees both `nested_decoder` and `seq_decoder` on error.
3927fn generate_nested_inline_field_decode_arena(
3928    output: &mut String,
3929    ty: &Type,
3930    var_name: &str,
3931    ind: &str,
3932    arena: &str,
3933) -> Result<(), Box<dyn std::error::Error>> {
3934    if let Some(call) = make_decode_call_arena(ty, "nested_decoder", arena, var_name) {
3935        writeln!(output, "{}err = {};", ind, call)?;
3936    } else {
3937        writeln!(
3938            output,
3939            "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported nested field type */",
3940            ind
3941        )?;
3942    }
3943    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3944    writeln!(output, "{}    synta_decoder_free(nested_decoder);", ind)?;
3945    writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
3946    writeln!(output, "{}    return err;", ind)?;
3947    writeln!(output, "{}}}", ind)?;
3948    emit_arena_track_field(output, ty, var_name, arena, ind)?;
3949    Ok(())
3950}
3951
3952/// Arena-aware version of `generate_choice_seq_field_decode`.
3953/// Uses `nested_decoder`; only frees `nested_decoder` on error (no outer seq_decoder).
3954fn generate_choice_seq_field_decode_arena(
3955    output: &mut String,
3956    ty: &Type,
3957    var_name: &str,
3958    ind: &str,
3959    arena: &str,
3960) -> Result<(), Box<dyn std::error::Error>> {
3961    if let Some(call) = make_decode_call_arena(ty, "nested_decoder", arena, var_name) {
3962        writeln!(output, "{}err = {};", ind, call)?;
3963    } else {
3964        writeln!(
3965            output,
3966            "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported nested field type */",
3967            ind
3968        )?;
3969    }
3970    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3971    writeln!(output, "{}    synta_decoder_free(nested_decoder);", ind)?;
3972    writeln!(output, "{}    return err;", ind)?;
3973    writeln!(output, "{}}}", ind)?;
3974    emit_arena_track_field(output, ty, var_name, arena, ind)?;
3975    Ok(())
3976}
3977
3978/// Arena-aware element decode for use inside a SEQUENCE OF field within a SEQUENCE body.
3979/// Frees `array`, `array_decoder`, and `seq_decoder` on error.
3980fn generate_decode_element_arena(
3981    output: &mut String,
3982    ty: &Type,
3983    decoder_name: &str,
3984    var_name: &str,
3985    ind: &str,
3986    arena: &str,
3987) -> Result<(), Box<dyn std::error::Error>> {
3988    if let Some(call) = make_decode_call_arena(ty, decoder_name, arena, var_name) {
3989        writeln!(output, "{}err = {};", ind, call)?;
3990    } else {
3991        writeln!(output, "{}return SyntaErrorCode_InvalidEncoding;", ind)?;
3992        return Ok(());
3993    }
3994    writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
3995    writeln!(output, "{}    free(array);", ind)?;
3996    writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
3997    writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
3998    writeln!(output, "{}    return err;", ind)?;
3999    writeln!(output, "{}}}", ind)?;
4000    emit_arena_track_field(output, ty, var_name, arena, ind)?;
4001    Ok(())
4002}
4003
4004/// Arena-aware element decode for top-level SEQUENCE OF / SET OF types.
4005/// Frees `array` and `array_decoder` on error (no outer `seq_decoder`).
4006fn generate_decode_element_toplevel_arena(
4007    output: &mut String,
4008    ty: &Type,
4009    decoder_name: &str,
4010    var_name: &str,
4011    arena: &str,
4012) -> Result<(), Box<dyn std::error::Error>> {
4013    if let Some(call) = make_decode_call_arena(ty, decoder_name, arena, var_name) {
4014        writeln!(output, "        err = {};", call)?;
4015    } else {
4016        writeln!(output, "        err = SyntaErrorCode_InvalidEncoding;")?;
4017    }
4018    writeln!(output, "        if (err != SyntaErrorCode_Success) {{")?;
4019    writeln!(output, "            free(array);")?;
4020    writeln!(output, "            synta_decoder_free(array_decoder);")?;
4021    writeln!(output, "            return err;")?;
4022    writeln!(output, "        }}")?;
4023    emit_arena_track_field(output, ty, var_name, arena, "        ")?;
4024    Ok(())
4025}
4026
4027/// Arena-aware field decode inside a SEQUENCE/SET body.
4028///
4029/// Mirrors `generate_field_decode` but:
4030/// - Uses `make_decode_call_arena` (TypeRef → `_decode_arena` variant).
4031/// - Emits `_synta_arena_track` calls after each successful pointer allocation.
4032/// - For inline SEQUENCE/SET fields, recurses with `generate_nested_inline_field_decode_arena`.
4033/// - For SEQUENCE OF/SET OF fields, tracks the array pointer with `free`.
4034fn generate_field_decode_arena(
4035    output: &mut String,
4036    ty: &Type,
4037    var_name: &str,
4038    indent: usize,
4039    arena: &str,
4040    defs: &[Definition],
4041) -> Result<(), Box<dyn std::error::Error>> {
4042    let ind = "    ".repeat(indent);
4043
4044    if let Some(call) = make_decode_call_arena(ty, "seq_decoder", arena, var_name) {
4045        writeln!(output, "{}err = {};", ind, call)?;
4046        writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
4047        writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4048        writeln!(output, "{}    return err;", ind)?;
4049        writeln!(output, "{}}}", ind)?;
4050        emit_arena_track_field(output, ty, var_name, arena, &ind)?;
4051    } else {
4052        match ty {
4053            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
4054                let is_sequence = matches!(ty, Type::Sequence(_));
4055                let container_type = if is_sequence { "sequence" } else { "set" };
4056
4057                writeln!(
4058                    output,
4059                    "{}// Decode nested {}",
4060                    ind,
4061                    container_type.to_uppercase()
4062                )?;
4063                writeln!(output, "{}SyntaDecoder* nested_decoder = NULL;", ind)?;
4064                writeln!(
4065                    output,
4066                    "{}err = synta_decoder_enter_{}(seq_decoder, &nested_decoder);",
4067                    ind, container_type
4068                )?;
4069                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
4070                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4071                writeln!(output, "{}    return err;", ind)?;
4072                writeln!(output, "{}}}", ind)?;
4073                writeln!(output)?;
4074
4075                for inner_field in inner_fields {
4076                    if matches!(inner_field.ty, Type::Null) {
4077                        continue;
4078                    }
4079
4080                    let inner_name = to_snake_case(&inner_field.name);
4081                    let nested_var = format!("{}.{}", var_name, inner_name);
4082                    if inner_field.optional {
4083                        writeln!(
4084                            output,
4085                            "{}// Decode optional nested field: {}",
4086                            ind, inner_field.name
4087                        )?;
4088                        let tag_check = get_tag_check_condition(&inner_field.ty, defs);
4089                        writeln!(output, "{}{{", ind)?;
4090                        writeln!(output, "{}    SyntaTag tag;", ind)?;
4091                        writeln!(
4092                            output,
4093                            "{}    if (synta_decoder_peek_tag(nested_decoder, &tag) == SyntaErrorCode_Success &&",
4094                            ind
4095                        )?;
4096                        writeln!(output, "{}        ({})) {{", ind, tag_check)?;
4097                        let deeper_ind = format!("{}        ", ind);
4098                        generate_nested_inline_field_decode_arena(
4099                            output,
4100                            &inner_field.ty,
4101                            &nested_var,
4102                            &deeper_ind,
4103                            arena,
4104                        )?;
4105                        writeln!(
4106                            output,
4107                            "{}        {}.has_{} = true;",
4108                            ind, var_name, inner_name
4109                        )?;
4110                        writeln!(output, "{}    }} else {{", ind)?;
4111                        writeln!(
4112                            output,
4113                            "{}        {}.has_{} = false;",
4114                            ind, var_name, inner_name
4115                        )?;
4116                        writeln!(output, "{}    }}", ind)?;
4117                        writeln!(output, "{}}}", ind)?;
4118                    } else {
4119                        writeln!(output, "{}// Decode field: {}", ind, inner_field.name)?;
4120                        generate_nested_inline_field_decode_arena(
4121                            output,
4122                            &inner_field.ty,
4123                            &nested_var,
4124                            &ind,
4125                            arena,
4126                        )?;
4127                    }
4128                    writeln!(output)?;
4129                }
4130
4131                writeln!(output, "{}synta_decoder_free(nested_decoder);", ind)?;
4132            }
4133            Type::SequenceOf(inner_ty, _) | Type::SetOf(inner_ty, _) => {
4134                let is_sequence = matches!(ty, Type::SequenceOf(_, _));
4135                let container_type = if is_sequence { "sequence" } else { "set" };
4136
4137                writeln!(output, "{}// Decode SEQUENCE OF / SET OF", ind)?;
4138                writeln!(output, "{}SyntaDecoder* array_decoder = NULL;", ind)?;
4139                writeln!(
4140                    output,
4141                    "{}err = synta_decoder_enter_{}(seq_decoder, &array_decoder);",
4142                    ind, container_type
4143                )?;
4144                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
4145                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4146                writeln!(output, "{}    return err;", ind)?;
4147                writeln!(output, "{}}}", ind)?;
4148                writeln!(output)?;
4149
4150                writeln!(output, "{}// Allocate dynamic array for elements", ind)?;
4151                writeln!(output, "{}size_t capacity = 4; // Initial capacity", ind)?;
4152                writeln!(output, "{}size_t count = 0;", ind)?;
4153                let elem_c_type = get_c_type(inner_ty);
4154                let array_type = format!("{}*", elem_c_type);
4155                writeln!(
4156                    output,
4157                    "{}{} array = ({})calloc(capacity, sizeof({}));",
4158                    ind, array_type, array_type, elem_c_type
4159                )?;
4160                writeln!(output, "{}if (array == NULL) {{", ind)?;
4161                writeln!(output, "{}    synta_decoder_free(array_decoder);", ind)?;
4162                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4163                writeln!(output, "{}    return SyntaErrorCode_OutOfMemory;", ind)?;
4164                writeln!(output, "{}}}", ind)?;
4165                writeln!(output)?;
4166
4167                writeln!(
4168                    output,
4169                    "{}while (!synta_decoder_at_end(array_decoder)) {{",
4170                    ind
4171                )?;
4172                writeln!(output, "{}    // Grow array if needed", ind)?;
4173                writeln!(output, "{}    if (count >= capacity) {{", ind)?;
4174                writeln!(output, "{}        capacity *= 2;", ind)?;
4175                writeln!(
4176                    output,
4177                    "{}        {} new_array = ({})realloc(array, capacity * sizeof({}));",
4178                    ind, array_type, array_type, elem_c_type
4179                )?;
4180                writeln!(output, "{}        if (new_array == NULL) {{", ind)?;
4181                writeln!(output, "{}            free(array);", ind)?;
4182                writeln!(
4183                    output,
4184                    "{}            synta_decoder_free(array_decoder);",
4185                    ind
4186                )?;
4187                writeln!(
4188                    output,
4189                    "{}            synta_decoder_free(seq_decoder);",
4190                    ind
4191                )?;
4192                writeln!(
4193                    output,
4194                    "{}            return SyntaErrorCode_OutOfMemory;",
4195                    ind
4196                )?;
4197                writeln!(output, "{}        }}", ind)?;
4198                writeln!(output, "{}        array = new_array;", ind)?;
4199                writeln!(output, "{}    }}", ind)?;
4200                writeln!(output)?;
4201
4202                writeln!(output, "{}    // Decode element", ind)?;
4203                generate_decode_element_arena(
4204                    output,
4205                    inner_ty,
4206                    "array_decoder",
4207                    "array[count]",
4208                    "    ",
4209                    arena,
4210                )?;
4211                writeln!(output, "{}    count++;", ind)?;
4212                writeln!(output, "{}}}", ind)?;
4213                writeln!(output)?;
4214
4215                writeln!(output, "{}{} = array;", ind, var_name)?;
4216                writeln!(output, "{}{}_count = count;", ind, var_name)?;
4217                writeln!(output, "{}synta_decoder_free(array_decoder);", ind)?;
4218                // Track the array pointer with free
4219                writeln!(
4220                    output,
4221                    "{}_synta_arena_track({}, {}, free);",
4222                    ind, arena, var_name
4223                )?;
4224            }
4225            Type::Tagged {
4226                tag: tag_info,
4227                inner,
4228            } => match tag_info.tagging {
4229                Tagging::Explicit => {
4230                    let class = tag_class_c_str(&tag_info.class);
4231                    writeln!(
4232                        output,
4233                        "{}// Enter EXPLICIT [{cls} {num}] tag wrapper",
4234                        ind,
4235                        cls = class,
4236                        num = tag_info.number
4237                    )?;
4238                    writeln!(output, "{}{{", ind)?;
4239                    writeln!(output, "{}    SyntaDecoder* tagged_decoder = NULL;", ind)?;
4240                    writeln!(output, "{}    SyntaTag explicit_tag;", ind)?;
4241                    writeln!(output, "{}    explicit_tag.class_ = {};", ind, class)?;
4242                    writeln!(output, "{}    explicit_tag.constructed = true;", ind)?;
4243                    writeln!(
4244                        output,
4245                        "{}    explicit_tag.number = {};",
4246                        ind, tag_info.number
4247                    )?;
4248                    writeln!(
4249                        output,
4250                        "{}    err = synta_decoder_enter_constructed(seq_decoder, explicit_tag, &tagged_decoder);",
4251                        ind
4252                    )?;
4253                    writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
4254                    writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
4255                    writeln!(output, "{}        return err;", ind)?;
4256                    writeln!(output, "{}    }}", ind)?;
4257                    if let Some(call) =
4258                        make_decode_call_arena(inner, "tagged_decoder", arena, var_name)
4259                    {
4260                        writeln!(output, "{}    err = {};", ind, call)?;
4261                        writeln!(output, "{}    if (err != SyntaErrorCode_Success) {{", ind)?;
4262                        writeln!(output, "{}        synta_decoder_free(tagged_decoder);", ind)?;
4263                        writeln!(output, "{}        synta_decoder_free(seq_decoder);", ind)?;
4264                        writeln!(output, "{}        return err;", ind)?;
4265                        writeln!(output, "{}    }}", ind)?;
4266                        emit_arena_track_field(
4267                            output,
4268                            inner,
4269                            var_name,
4270                            arena,
4271                            &format!("{}    ", ind),
4272                        )?;
4273                    } else {
4274                        writeln!(output, "{}    /* structural inner type inside EXPLICIT tag: not yet supported */", ind)?;
4275                        writeln!(output, "{}    err = SyntaErrorCode_InvalidEncoding;", ind)?;
4276                        writeln!(output, "{}    synta_decoder_free(tagged_decoder);", ind)?;
4277                        writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4278                        writeln!(output, "{}    return err;", ind)?;
4279                    }
4280                    writeln!(output, "{}    synta_decoder_free(tagged_decoder);", ind)?;
4281                    writeln!(output, "{}}}", ind)?;
4282                }
4283                Tagging::Implicit => {
4284                    let class = tag_class_c_str(&tag_info.class);
4285                    writeln!(
4286                        output,
4287                        "{}/* IMPLICIT [{cls} {num}]: decoded transparently (tag check relaxed) */",
4288                        ind,
4289                        cls = class,
4290                        num = tag_info.number
4291                    )?;
4292                    generate_field_decode_arena(output, inner, var_name, indent, arena, defs)?;
4293                }
4294            },
4295            Type::Constrained {
4296                base_type: inner, ..
4297            } => {
4298                generate_field_decode_arena(output, inner, var_name, indent, arena, defs)?;
4299            }
4300            _ => {
4301                writeln!(
4302                    output,
4303                    "{}err = SyntaErrorCode_InvalidEncoding; /* unsupported field type */",
4304                    ind
4305                )?;
4306                writeln!(output, "{}if (err != SyntaErrorCode_Success) {{", ind)?;
4307                writeln!(output, "{}    synta_decoder_free(seq_decoder);", ind)?;
4308                writeln!(output, "{}    return err;", ind)?;
4309                writeln!(output, "{}}}", ind)?;
4310            }
4311        }
4312    }
4313
4314    Ok(())
4315}
4316
4317/// Generate the `_decode_arena` function for a SEQUENCE or SET type.
4318///
4319/// The generated function is identical in structure to `_decode` but passes
4320/// `arena` through every decode call and registers each heap-allocated handle
4321/// with `_synta_arena_track`.  The `_encode` and `_free` functions are NOT
4322/// re-generated — callers should still use the regular variants.
4323fn generate_sequence_arena_impl(
4324    output: &mut String,
4325    name: &str,
4326    fields: &[SequenceField],
4327    defs: &[Definition],
4328    is_set: bool,
4329) -> Result<(), Box<dyn std::error::Error>> {
4330    let struct_name = to_pascal_case(name);
4331    let fn_prefix = to_snake_case(name);
4332    let container_kind = if is_set { "set" } else { "sequence" };
4333    let enter_fn = if is_set {
4334        "synta_decoder_enter_set"
4335    } else {
4336        "synta_decoder_enter_sequence"
4337    };
4338
4339    writeln!(
4340        output,
4341        "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out) {{",
4342        fn_prefix, struct_name
4343    )?;
4344    writeln!(
4345        output,
4346        "    if (decoder == NULL || arena == NULL || out == NULL) {{"
4347    )?;
4348    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
4349    writeln!(output, "    }}")?;
4350    writeln!(output)?;
4351    writeln!(output, "    // Enter {}", container_kind.to_uppercase())?;
4352    writeln!(output, "    SyntaDecoder* seq_decoder = NULL;")?;
4353    writeln!(
4354        output,
4355        "    SyntaErrorCode err = {}(decoder, &seq_decoder);",
4356        enter_fn
4357    )?;
4358    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
4359    writeln!(output, "        return err;")?;
4360    writeln!(output, "    }}")?;
4361    writeln!(output)?;
4362
4363    for field in fields {
4364        let field_name = to_snake_case(&field.name);
4365
4366        if field.optional {
4367            writeln!(output, "    // Decode optional field: {}", field_name)?;
4368            let tag_check = get_tag_check_condition(&field.ty, defs);
4369            writeln!(output, "    {{")?;
4370            writeln!(output, "        SyntaTag tag;")?;
4371            writeln!(
4372                output,
4373                "        if (synta_decoder_peek_tag(seq_decoder, &tag) == SyntaErrorCode_Success &&"
4374            )?;
4375            writeln!(output, "            ({})) {{", tag_check)?;
4376            generate_field_decode_arena(
4377                output,
4378                &field.ty,
4379                &format!("out->{}", field_name),
4380                3,
4381                "arena",
4382                defs,
4383            )?;
4384            writeln!(output, "            out->has_{} = true;", field_name)?;
4385            writeln!(output, "        }} else {{")?;
4386            writeln!(output, "            out->has_{} = false;", field_name)?;
4387            writeln!(output, "        }}")?;
4388            writeln!(output, "    }}")?;
4389        } else {
4390            writeln!(output, "    // Decode field: {}", field_name)?;
4391            generate_field_decode_arena(
4392                output,
4393                &field.ty,
4394                &format!("out->{}", field_name),
4395                1,
4396                "arena",
4397                defs,
4398            )?;
4399        }
4400        writeln!(output)?;
4401    }
4402
4403    writeln!(output, "    synta_decoder_free(seq_decoder);")?;
4404    writeln!(output, "    return SyntaErrorCode_Success;")?;
4405    writeln!(output, "}}")?;
4406
4407    Ok(())
4408}
4409
4410/// Generate the `_decode_arena` function for a CHOICE type.
4411fn generate_choice_arena_impl(
4412    output: &mut String,
4413    name: &str,
4414    variants: &[ChoiceVariant],
4415    defs: &[Definition],
4416) -> Result<(), Box<dyn std::error::Error>> {
4417    let struct_name = to_pascal_case(name);
4418    let fn_prefix = to_snake_case(name);
4419    let tag_enum_name = format!("{}Tag", struct_name);
4420
4421    writeln!(
4422        output,
4423        "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out) {{",
4424        fn_prefix, struct_name
4425    )?;
4426    writeln!(
4427        output,
4428        "    if (decoder == NULL || arena == NULL || out == NULL) {{"
4429    )?;
4430    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
4431    writeln!(output, "    }}")?;
4432    writeln!(output)?;
4433    writeln!(
4434        output,
4435        "    // Peek at the next tag to determine which variant"
4436    )?;
4437    writeln!(output, "    SyntaTag tag;")?;
4438    writeln!(
4439        output,
4440        "    SyntaErrorCode err = synta_decoder_peek_tag(decoder, &tag);"
4441    )?;
4442    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
4443    writeln!(output, "        return err;")?;
4444    writeln!(output, "    }}")?;
4445    writeln!(output)?;
4446
4447    for (i, variant) in variants.iter().enumerate() {
4448        let variant_name = to_snake_case(&variant.name);
4449        let variant_pascal = to_pascal_case(&variant.name);
4450        let tag_check = get_tag_check_condition(&variant.ty, defs);
4451
4452        let else_if = if i == 0 { "if" } else { "} else if" };
4453
4454        writeln!(output, "    {} ({}) {{", else_if, tag_check)?;
4455        writeln!(output, "        // Decode {} variant", variant_name)?;
4456        writeln!(
4457            output,
4458            "        out->tag = {}_{};",
4459            tag_enum_name, variant_pascal
4460        )?;
4461        generate_choice_variant_decode_arena(
4462            output,
4463            &variant.ty,
4464            &format!("out->value.{}", variant_name),
4465            "arena",
4466        )?;
4467        writeln!(output, "        return SyntaErrorCode_Success;")?;
4468    }
4469
4470    writeln!(output, "    }} else {{")?;
4471    writeln!(
4472        output,
4473        "        /* no matching variant found for the observed tag */"
4474    )?;
4475    writeln!(output, "        return SyntaErrorCode_InvalidEncoding;")?;
4476    writeln!(output, "    }}")?;
4477    writeln!(output, "}}")?;
4478
4479    Ok(())
4480}
4481
4482/// Emit arena-aware decode code for one CHOICE variant.
4483fn generate_choice_variant_decode_arena(
4484    output: &mut String,
4485    ty: &Type,
4486    var_name: &str,
4487    arena: &str,
4488) -> Result<(), Box<dyn std::error::Error>> {
4489    if let Some(call) = make_decode_call_arena(ty, "decoder", arena, var_name) {
4490        writeln!(output, "        err = {};", call)?;
4491        writeln!(
4492            output,
4493            "        if (err != SyntaErrorCode_Success) return err;"
4494        )?;
4495        emit_arena_track_field(output, ty, var_name, arena, "        ")?;
4496    } else {
4497        match ty {
4498            Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
4499                let is_sequence = matches!(ty, Type::Sequence(_));
4500                let container_type = if is_sequence { "sequence" } else { "set" };
4501                writeln!(
4502                    output,
4503                    "        /* Decode nested {} choice variant */",
4504                    container_type.to_uppercase()
4505                )?;
4506                writeln!(output, "        SyntaDecoder* nested_decoder = NULL;")?;
4507                writeln!(
4508                    output,
4509                    "        err = synta_decoder_enter_{}(decoder, &nested_decoder);",
4510                    container_type
4511                )?;
4512                writeln!(
4513                    output,
4514                    "        if (err != SyntaErrorCode_Success) return err;"
4515                )?;
4516                writeln!(output)?;
4517                for inner_field in inner_fields {
4518                    if matches!(inner_field.ty, Type::Null) {
4519                        continue;
4520                    }
4521                    let inner_name = to_snake_case(&inner_field.name);
4522                    let nested_var = format!("{}.{}", var_name, inner_name);
4523                    generate_choice_seq_field_decode_arena(
4524                        output,
4525                        &inner_field.ty,
4526                        &nested_var,
4527                        "        ",
4528                        arena,
4529                    )?;
4530                    writeln!(output)?;
4531                }
4532                writeln!(output, "        synta_decoder_free(nested_decoder);")?;
4533            }
4534            _ => {
4535                writeln!(output, "        return SyntaErrorCode_InvalidEncoding;")?;
4536            }
4537        }
4538    }
4539    Ok(())
4540}
4541
4542/// Generate the `_decode_arena` function for a top-level SEQUENCE OF / SET OF type.
4543fn generate_array_arena_impl(
4544    output: &mut String,
4545    name: &str,
4546    inner_ty: &Type,
4547    is_sequence: bool,
4548    size_constraint: Option<ArraySizeBounds>,
4549) -> Result<(), Box<dyn std::error::Error>> {
4550    let struct_name = to_pascal_case(name);
4551    let fn_prefix = to_snake_case(name);
4552    let container_type = if is_sequence { "sequence" } else { "set" };
4553    let elem_c_type = get_c_type(inner_ty);
4554    let array_type = format!("{}*", elem_c_type);
4555
4556    writeln!(
4557        output,
4558        "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, struct {}* out) {{",
4559        fn_prefix, struct_name
4560    )?;
4561    writeln!(
4562        output,
4563        "    if (decoder == NULL || arena == NULL || out == NULL) {{"
4564    )?;
4565    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
4566    writeln!(output, "    }}")?;
4567    writeln!(output)?;
4568
4569    writeln!(output, "    // Enter SEQUENCE/SET")?;
4570    writeln!(output, "    SyntaDecoder* array_decoder = NULL;")?;
4571    writeln!(
4572        output,
4573        "    SyntaErrorCode err = synta_decoder_enter_{}(decoder, &array_decoder);",
4574        container_type
4575    )?;
4576    writeln!(output, "    if (err != SyntaErrorCode_Success) {{")?;
4577    writeln!(output, "        return err;")?;
4578    writeln!(output, "    }}")?;
4579    writeln!(output)?;
4580
4581    writeln!(output, "    // Allocate dynamic array for elements")?;
4582    writeln!(output, "    size_t capacity = 4; // Initial capacity")?;
4583    writeln!(output, "    size_t count = 0;")?;
4584    writeln!(
4585        output,
4586        "    {} array = ({})calloc(capacity, sizeof({}));",
4587        array_type, array_type, elem_c_type
4588    )?;
4589    writeln!(output, "    if (array == NULL) {{")?;
4590    writeln!(output, "        synta_decoder_free(array_decoder);")?;
4591    writeln!(output, "        return SyntaErrorCode_OutOfMemory;")?;
4592    writeln!(output, "    }}")?;
4593    writeln!(output)?;
4594
4595    writeln!(
4596        output,
4597        "    while (!synta_decoder_at_end(array_decoder)) {{"
4598    )?;
4599    writeln!(output, "        // Grow array if needed")?;
4600    writeln!(output, "        if (count >= capacity) {{")?;
4601    writeln!(output, "            capacity *= 2;")?;
4602    writeln!(
4603        output,
4604        "            {} new_array = ({})realloc(array, capacity * sizeof({}));",
4605        array_type, array_type, elem_c_type
4606    )?;
4607    writeln!(output, "            if (new_array == NULL) {{")?;
4608    writeln!(output, "                free(array);")?;
4609    writeln!(output, "                synta_decoder_free(array_decoder);")?;
4610    writeln!(output, "                return SyntaErrorCode_OutOfMemory;")?;
4611    writeln!(output, "            }}")?;
4612    writeln!(output, "            array = new_array;")?;
4613    writeln!(output, "        }}")?;
4614    writeln!(output)?;
4615
4616    writeln!(output, "        // Decode element")?;
4617    generate_decode_element_toplevel_arena(
4618        output,
4619        inner_ty,
4620        "array_decoder",
4621        "array[count]",
4622        "arena",
4623    )?;
4624    writeln!(output, "        count++;")?;
4625    writeln!(output, "    }}")?;
4626    writeln!(output)?;
4627
4628    // SIZE constraint check after the decode loop
4629    if let Some(size_spec) = size_constraint {
4630        emit_array_size_check(output, size_spec, "count")?;
4631    }
4632
4633    writeln!(output, "    out->count = count;")?;
4634    writeln!(output, "    out->items = array;")?;
4635    writeln!(output, "    _synta_arena_track(arena, out->items, free);")?;
4636    writeln!(output, "    synta_decoder_free(array_decoder);")?;
4637    writeln!(output, "    return SyntaErrorCode_Success;")?;
4638    writeln!(output, "}}")?;
4639
4640    Ok(())
4641}
4642
4643/// Generate the `_decode_arena` function for an enumerated or named-integer type.
4644///
4645/// Enums decode into an `int64_t` on the stack (the temporary `SyntaInteger*`
4646/// is freed immediately), so no arena tracking is needed.  The `arena` parameter
4647/// is accepted but marked unused so the caller's code compiles cleanly.
4648fn generate_enum_arena_impl(
4649    output: &mut String,
4650    name: &str,
4651) -> Result<(), Box<dyn std::error::Error>> {
4652    let fn_prefix = to_snake_case(name);
4653
4654    writeln!(
4655        output,
4656        "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, enum {}* out) {{",
4657        fn_prefix,
4658        to_pascal_case(name)
4659    )?;
4660    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
4661    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
4662    writeln!(output, "    }}")?;
4663    writeln!(
4664        output,
4665        "    (void)arena; /* enum decode does not leave heap allocations */"
4666    )?;
4667    writeln!(output)?;
4668    writeln!(output, "    SyntaInteger* int_val = NULL;")?;
4669    writeln!(
4670        output,
4671        "    SyntaErrorCode err = synta_decode_integer(decoder, &int_val);"
4672    )?;
4673    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4674    writeln!(output)?;
4675    writeln!(output, "    int64_t val;")?;
4676    writeln!(output, "    err = synta_integer_to_i64(int_val, &val);")?;
4677    writeln!(output, "    synta_integer_free(int_val);")?;
4678    writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4679    writeln!(output)?;
4680    writeln!(output, "    *out = (enum {})val;", to_pascal_case(name))?;
4681    writeln!(output, "    return SyntaErrorCode_Success;")?;
4682    writeln!(output, "}}")?;
4683
4684    Ok(())
4685}
4686
4687/// Generate the `_decode_arena` function for a simple type alias.
4688///
4689/// For value types (Boolean, Real, Null) no tracking is needed.
4690/// For pointer types (Integer, OctetString, OID) the result is registered
4691/// with the arena after a successful decode.
4692/// For type aliases (TypeRef) the call is forwarded to `{ref}_decode_arena`.
4693fn generate_simple_arena_impl(
4694    output: &mut String,
4695    name: &str,
4696    ty: &Type,
4697) -> Result<(), Box<dyn std::error::Error>> {
4698    let fn_prefix = to_snake_case(name);
4699    let type_name = to_pascal_case(name);
4700    let base = unwrap_type(ty);
4701
4702    writeln!(
4703        output,
4704        "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out) {{",
4705        fn_prefix, type_name
4706    )?;
4707    writeln!(output, "    if (decoder == NULL || out == NULL) {{")?;
4708    writeln!(output, "        return SyntaErrorCode_NullPointer;")?;
4709    writeln!(output, "    }}")?;
4710
4711    match base {
4712        Type::Boolean => {
4713            writeln!(output, "    (void)arena;")?;
4714            writeln!(output, "    return synta_decode_boolean(decoder, out);")?;
4715        }
4716        Type::Integer(_, _) | Type::Enumerated(_) => {
4717            writeln!(
4718                output,
4719                "    SyntaErrorCode err = synta_decode_integer(decoder, out);"
4720            )?;
4721            writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4722            writeln!(
4723                output,
4724                "    _synta_arena_track(arena, *out, (void(*)(void*))synta_integer_free);"
4725            )?;
4726            writeln!(output, "    return SyntaErrorCode_Success;")?;
4727        }
4728        Type::Real => {
4729            writeln!(output, "    (void)arena;")?;
4730            writeln!(output, "    return synta_decode_real(decoder, out);")?;
4731        }
4732        Type::Null => {
4733            writeln!(output, "    (void)arena; (void)out;")?;
4734            writeln!(output, "    return synta_decode_null(decoder);")?;
4735        }
4736        Type::ObjectIdentifier => {
4737            writeln!(
4738                output,
4739                "    SyntaErrorCode err = synta_decode_object_identifier(decoder, out);"
4740            )?;
4741            writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4742            writeln!(
4743                output,
4744                "    _synta_arena_track(arena, *out, (void(*)(void*))synta_oid_free);"
4745            )?;
4746            writeln!(output, "    return SyntaErrorCode_Success;")?;
4747        }
4748        Type::BitString(_) => {
4749            writeln!(
4750                output,
4751                "    SyntaErrorCode err = synta_decode_bit_string(decoder, &out->data, &out->unused_bits);"
4752            )?;
4753            writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4754            writeln!(
4755                output,
4756                "    if (out->data.owned) _synta_arena_track(arena, (void*)out->data.data, free);"
4757            )?;
4758            writeln!(output, "    return SyntaErrorCode_Success;")?;
4759        }
4760        Type::OctetString(_)
4761        | Type::Utf8String(_)
4762        | Type::PrintableString(_)
4763        | Type::IA5String(_)
4764        | Type::UtcTime
4765        | Type::GeneralizedTime
4766        | Type::Any
4767        | Type::AnyDefinedBy(_) => {
4768            writeln!(
4769                output,
4770                "    SyntaErrorCode err = synta_decode_octet_string(decoder, out);"
4771            )?;
4772            writeln!(output, "    if (err != SyntaErrorCode_Success) return err;")?;
4773            writeln!(
4774                output,
4775                "    _synta_arena_track(arena, *out, (void(*)(void*))synta_octet_string_free);"
4776            )?;
4777            writeln!(output, "    return SyntaErrorCode_Success;")?;
4778        }
4779        Type::TypeRef(ref_name) => {
4780            let ref_fn = to_snake_case(ref_name);
4781            writeln!(
4782                output,
4783                "    return {}_decode_arena(decoder, arena, out);",
4784                ref_fn
4785            )?;
4786        }
4787        _ => {
4788            writeln!(output, "    /* unsupported simple type alias */")?;
4789            writeln!(output, "    return SyntaErrorCode_InvalidEncoding;")?;
4790        }
4791    }
4792    writeln!(output, "}}")?;
4793    writeln!(output)?;
4794
4795    Ok(())
4796}
4797
4798#[cfg(test)]
4799mod tests {
4800    use super::*;
4801
4802    fn make_module(name: &str, ty: Type) -> Module {
4803        Module {
4804            name: "TestModule".to_string(),
4805            oid: None,
4806            values: vec![],
4807            tagging_mode: None,
4808            imports: vec![],
4809            exports: vec![],
4810            definitions: vec![Definition {
4811                name: name.to_string(),
4812                ty,
4813            }],
4814        }
4815    }
4816
4817    fn impl_config() -> CImplConfig {
4818        CImplConfig {
4819            header_file: "test.h".to_string(),
4820            arena_mode: false,
4821            pattern_mode: PatternMode::Skip,
4822            with_containing: false,
4823        }
4824    }
4825
4826    // -----------------------------------------------------------------------
4827    // No SIZE constraint → no check emitted
4828    // -----------------------------------------------------------------------
4829
4830    #[test]
4831    fn sequence_of_no_size_constraint_no_check() {
4832        let module = make_module(
4833            "Items",
4834            Type::SequenceOf(Box::new(Type::Integer(None, vec![])), None),
4835        );
4836        let result = generate_c_impl(&module, impl_config()).unwrap();
4837        // Basic structure present
4838        assert!(result.contains("items_decode"), "decode function generated");
4839        assert!(result.contains("items_encode"), "encode function generated");
4840        // No SIZE check emitted
4841        assert!(
4842            !result.contains("Validate SIZE constraint"),
4843            "no SIZE check without constraint"
4844        );
4845    }
4846
4847    // -----------------------------------------------------------------------
4848    // Fixed SIZE (e.g. SIZE (3)) → exact count check on decode and encode
4849    // -----------------------------------------------------------------------
4850
4851    #[test]
4852    fn sequence_of_fixed_size_generates_check() {
4853        let module = make_module(
4854            "Triple",
4855            Type::SequenceOf(
4856                Box::new(Type::Integer(None, vec![])),
4857                Some(SizeConstraint::Fixed(3)),
4858            ),
4859        );
4860        let result = generate_c_impl(&module, impl_config()).unwrap();
4861        // Decode-side: check after loop
4862        assert!(
4863            result.contains("< (size_t)3ULL") || result.contains("> (size_t)3ULL"),
4864            "fixed SIZE check on decode"
4865        );
4866        // Encode-side: check before encode loop
4867        assert!(result.contains("value->count"), "encode-side count checked");
4868        // Both bounds present for exact count
4869        assert!(result.contains("< (size_t)3ULL"), "lower bound (count < 3)");
4870        assert!(result.contains("> (size_t)3ULL"), "upper bound (count > 3)");
4871    }
4872
4873    // -----------------------------------------------------------------------
4874    // Range with lower bound 1 and no upper bound → only lower bound check
4875    // -----------------------------------------------------------------------
4876
4877    #[test]
4878    fn sequence_of_range_lower_only() {
4879        let module = make_module(
4880            "NonEmpty",
4881            Type::SequenceOf(
4882                Box::new(Type::Integer(None, vec![])),
4883                Some(SizeConstraint::Range(Some(1), None)),
4884            ),
4885        );
4886        let result = generate_c_impl(&module, impl_config()).unwrap();
4887        // Lower bound check
4888        assert!(result.contains("< (size_t)1ULL"), "lower bound emitted");
4889        // No upper bound check
4890        assert!(!result.contains("> (size_t)"), "no upper bound emitted");
4891    }
4892
4893    // -----------------------------------------------------------------------
4894    // Range with lower bound 0 → lower bound skipped (always satisfied)
4895    // -----------------------------------------------------------------------
4896
4897    #[test]
4898    fn sequence_of_range_zero_lower_bound_skipped() {
4899        let module = make_module(
4900            "Items",
4901            Type::SequenceOf(
4902                Box::new(Type::Integer(None, vec![])),
4903                Some(SizeConstraint::Range(Some(0), Some(10))),
4904            ),
4905        );
4906        let result = generate_c_impl(&module, impl_config()).unwrap();
4907        // Upper bound check present
4908        assert!(result.contains("> (size_t)10ULL"), "upper bound emitted");
4909        // Lower bound (0) skipped — `< (size_t)0ULL` would always be false
4910        assert!(
4911            !result.contains("< (size_t)0ULL"),
4912            "zero lower bound is not emitted"
4913        );
4914    }
4915
4916    // -----------------------------------------------------------------------
4917    // Range with both lower and upper bounds
4918    // -----------------------------------------------------------------------
4919
4920    #[test]
4921    fn sequence_of_range_both_bounds() {
4922        let module = make_module(
4923            "Items",
4924            Type::SequenceOf(
4925                Box::new(Type::Integer(None, vec![])),
4926                Some(SizeConstraint::Range(Some(2), Some(5))),
4927            ),
4928        );
4929        let result = generate_c_impl(&module, impl_config()).unwrap();
4930        assert!(result.contains("< (size_t)2ULL"), "lower bound emitted");
4931        assert!(result.contains("> (size_t)5ULL"), "upper bound emitted");
4932        // Both on same line (joined with ||)
4933        assert!(result.contains("||"), "conditions joined with ||");
4934    }
4935
4936    // -----------------------------------------------------------------------
4937    // Encode-side check uses value->count, not count
4938    // -----------------------------------------------------------------------
4939
4940    #[test]
4941    fn sequence_of_encode_side_check_uses_value_count() {
4942        let module = make_module(
4943            "Items",
4944            Type::SequenceOf(
4945                Box::new(Type::Integer(None, vec![])),
4946                Some(SizeConstraint::Range(Some(1), Some(4))),
4947            ),
4948        );
4949        let result = generate_c_impl(&module, impl_config()).unwrap();
4950        assert!(
4951            result.contains("value->count < (size_t)1ULL"),
4952            "encode-side lower bound uses value->count"
4953        );
4954        assert!(
4955            result.contains("value->count > (size_t)4ULL"),
4956            "encode-side upper bound uses value->count"
4957        );
4958    }
4959
4960    // -----------------------------------------------------------------------
4961    // Decode-side check placed after decode loop, before out->count assignment
4962    // -----------------------------------------------------------------------
4963
4964    #[test]
4965    fn sequence_of_decode_check_before_assignment() {
4966        let module = make_module(
4967            "Items",
4968            Type::SequenceOf(
4969                Box::new(Type::Integer(None, vec![])),
4970                Some(SizeConstraint::Range(Some(1), None)),
4971            ),
4972        );
4973        let result = generate_c_impl(&module, impl_config()).unwrap();
4974        // SIZE check must appear before "out->count = count;" assignment
4975        let check_pos = result
4976            .find("Validate SIZE constraint on collection")
4977            .expect("SIZE check present");
4978        let assign_pos = result
4979            .find("out->count = count;")
4980            .expect("assignment present");
4981        assert!(
4982            check_pos < assign_pos,
4983            "SIZE check comes before out->count assignment"
4984        );
4985    }
4986
4987    // -----------------------------------------------------------------------
4988    // Encode-side check placed before encode loop
4989    // -----------------------------------------------------------------------
4990
4991    #[test]
4992    fn sequence_of_encode_check_before_loop() {
4993        let module = make_module(
4994            "Items",
4995            Type::SequenceOf(
4996                Box::new(Type::Integer(None, vec![])),
4997                Some(SizeConstraint::Range(Some(1), None)),
4998            ),
4999        );
5000        let result = generate_c_impl(&module, impl_config()).unwrap();
5001        // SIZE check must appear before "for (size_t i = 0;" loop
5002        let check_pos = result
5003            .find("Validate SIZE constraint before encoding")
5004            .expect("encode-side SIZE check present");
5005        let loop_pos = result
5006            .find("for (size_t i = 0;")
5007            .expect("encode loop present");
5008        assert!(
5009            check_pos < loop_pos,
5010            "encode SIZE check comes before the encode for-loop"
5011        );
5012    }
5013
5014    // -----------------------------------------------------------------------
5015    // SET OF behaves the same as SEQUENCE OF for SIZE constraints
5016    // -----------------------------------------------------------------------
5017
5018    #[test]
5019    fn set_of_size_constraint_generates_check() {
5020        let module = make_module(
5021            "Items",
5022            Type::SetOf(
5023                Box::new(Type::Integer(None, vec![])),
5024                Some(SizeConstraint::Range(Some(1), Some(8))),
5025            ),
5026        );
5027        let result = generate_c_impl(&module, impl_config()).unwrap();
5028        assert!(result.contains("< (size_t)1ULL"), "lower bound for SET OF");
5029        assert!(result.contains("> (size_t)8ULL"), "upper bound for SET OF");
5030    }
5031
5032    // -----------------------------------------------------------------------
5033    // Type::Constrained wrapper with modern SIZE constraint
5034    // -----------------------------------------------------------------------
5035
5036    #[test]
5037    fn constrained_sequence_of_modern_size() {
5038        let module = make_module(
5039            "Items",
5040            Type::Constrained {
5041                base_type: Box::new(Type::SequenceOf(
5042                    Box::new(Type::Integer(None, vec![])),
5043                    None,
5044                )),
5045                constraint: Constraint {
5046                    spec: ConstraintSpec::Subtype(SubtypeConstraint::SizeConstraint(Box::new(
5047                        SubtypeConstraint::ValueRange {
5048                            min: ConstraintValue::Integer(2),
5049                            max: ConstraintValue::Integer(6),
5050                        },
5051                    ))),
5052                    exception: None,
5053                },
5054            },
5055        );
5056        let result = generate_c_impl(&module, impl_config()).unwrap();
5057        assert!(
5058            result.contains("< (size_t)2ULL"),
5059            "modern SIZE lower bound extracted"
5060        );
5061        assert!(
5062            result.contains("> (size_t)6ULL"),
5063            "modern SIZE upper bound extracted"
5064        );
5065    }
5066
5067    // -----------------------------------------------------------------------
5068    // legacy_size_to_bounds / modern_size_to_bounds unit tests
5069    // -----------------------------------------------------------------------
5070
5071    #[test]
5072    fn legacy_size_fixed_maps_to_equal_bounds() {
5073        let bounds = legacy_size_to_bounds(&SizeConstraint::Fixed(7));
5074        assert_eq!(bounds, (Some(7), Some(7)));
5075    }
5076
5077    #[test]
5078    fn legacy_size_range_maps_correctly() {
5079        let bounds = legacy_size_to_bounds(&SizeConstraint::Range(Some(1), Some(10)));
5080        assert_eq!(bounds, (Some(1), Some(10)));
5081    }
5082
5083    #[test]
5084    fn legacy_size_range_unbounded_max() {
5085        let bounds = legacy_size_to_bounds(&SizeConstraint::Range(Some(1), None));
5086        assert_eq!(bounds, (Some(1), None));
5087    }
5088
5089    #[test]
5090    fn modern_size_extracts_value_range() {
5091        let spec = ConstraintSpec::Subtype(SubtypeConstraint::SizeConstraint(Box::new(
5092            SubtypeConstraint::ValueRange {
5093                min: ConstraintValue::Integer(3),
5094                max: ConstraintValue::Integer(9),
5095            },
5096        )));
5097        let bounds = modern_size_to_bounds(&spec);
5098        assert_eq!(bounds, Some((Some(3), Some(9))));
5099    }
5100
5101    #[test]
5102    fn modern_size_non_size_constraint_returns_none() {
5103        let spec = ConstraintSpec::Subtype(SubtypeConstraint::ValueRange {
5104            min: ConstraintValue::Integer(0),
5105            max: ConstraintValue::Integer(100),
5106        });
5107        let bounds = modern_size_to_bounds(&spec);
5108        assert_eq!(bounds, None);
5109    }
5110
5111    // -----------------------------------------------------------------------
5112    // Permitted-alphabet (FROM) constraint validation — Task 4
5113    // -----------------------------------------------------------------------
5114
5115    fn make_constrained_sequence(field_base_ty: Type, constraint: SubtypeConstraint) -> Module {
5116        Module {
5117            name: "TestModule".to_string(),
5118            oid: None,
5119            values: vec![],
5120            tagging_mode: None,
5121            imports: vec![],
5122            exports: vec![],
5123            definitions: vec![Definition {
5124                name: "Msg".to_string(),
5125                ty: Type::Sequence(vec![SequenceField {
5126                    name: "field".to_string(),
5127                    ty: Type::Constrained {
5128                        base_type: Box::new(field_base_ty),
5129                        constraint: Constraint {
5130                            spec: ConstraintSpec::Subtype(constraint),
5131                            exception: None,
5132                        },
5133                    },
5134                    optional: false,
5135                    default: None,
5136                }]),
5137            }],
5138        }
5139    }
5140
5141    #[test]
5142    fn permitted_alphabet_ia5string_generates_loop() {
5143        let module = make_constrained_sequence(
5144            Type::IA5String(None),
5145            SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'A', max: 'Z' }]),
5146        );
5147        let result = generate_c_impl(&module, impl_config()).unwrap();
5148        assert!(
5149            result.contains("for (_ai = 0; _ai < _alen; _ai++)"),
5150            "alphabet loop emitted"
5151        );
5152        assert!(result.contains("unsigned char _c = _ap[_ai]"), "byte fetch");
5153        assert!(result.contains("_c >= 'A' && _c <= 'Z'"), "range A-Z");
5154        assert!(
5155            result.contains("SyntaErrorCode_InvalidArgument"),
5156            "returns InvalidArgument on bad char"
5157        );
5158    }
5159
5160    #[test]
5161    fn permitted_alphabet_single_char_uses_equality() {
5162        let module = make_constrained_sequence(
5163            Type::IA5String(None),
5164            SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'x', max: 'x' }]),
5165        );
5166        let result = generate_c_impl(&module, impl_config()).unwrap();
5167        assert!(result.contains("_c == 'x'"), "single char uses ==");
5168        assert!(!result.contains("_c >= 'x'"), "no range for single char");
5169    }
5170
5171    #[test]
5172    fn permitted_alphabet_multiple_ranges_joined_with_or() {
5173        let module = make_constrained_sequence(
5174            Type::IA5String(None),
5175            SubtypeConstraint::PermittedAlphabet(vec![
5176                CharRange { min: 'A', max: 'Z' },
5177                CharRange { min: '0', max: '9' },
5178            ]),
5179        );
5180        let result = generate_c_impl(&module, impl_config()).unwrap();
5181        assert!(result.contains("_c >= 'A' && _c <= 'Z'"), "first range");
5182        assert!(result.contains("_c >= '0' && _c <= '9'"), "second range");
5183        assert!(result.contains(" || "), "ranges joined with ||");
5184    }
5185
5186    #[test]
5187    fn permitted_alphabet_not_emitted_for_integer_field() {
5188        // PermittedAlphabet on an INTEGER base type must be silently skipped
5189        let module = make_constrained_sequence(
5190            Type::Integer(None, vec![]),
5191            SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'A', max: 'Z' }]),
5192        );
5193        let result = generate_c_impl(&module, impl_config()).unwrap();
5194        assert!(
5195            !result.contains("for (_ai = 0"),
5196            "no alphabet loop for INTEGER"
5197        );
5198    }
5199
5200    #[test]
5201    fn permitted_alphabet_uses_synta_octet_string_helpers() {
5202        let module = make_constrained_sequence(
5203            Type::PrintableString(None),
5204            SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'a', max: 'z' }]),
5205        );
5206        let result = generate_c_impl(&module, impl_config()).unwrap();
5207        assert!(
5208            result.contains("synta_octet_string_len("),
5209            "uses synta_octet_string_len"
5210        );
5211        assert!(
5212            result.contains("synta_octet_string_data("),
5213            "uses synta_octet_string_data"
5214        );
5215    }
5216
5217    #[test]
5218    fn intersection_emits_both_size_and_alphabet_checks() {
5219        // SIZE (1..64) FROM ('A'..'Z'): both checks must appear in encode
5220        let module = make_constrained_sequence(
5221            Type::IA5String(None),
5222            SubtypeConstraint::Intersection(vec![
5223                SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
5224                    min: ConstraintValue::Integer(1),
5225                    max: ConstraintValue::Integer(64),
5226                })),
5227                SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'A', max: 'Z' }]),
5228            ]),
5229        );
5230        let result = generate_c_impl(&module, impl_config()).unwrap();
5231        assert!(
5232            result.contains("Validate SIZE range constraint"),
5233            "SIZE check present"
5234        );
5235        assert!(
5236            result.contains("Validate FROM (permitted alphabet) constraint"),
5237            "alphabet check present"
5238        );
5239    }
5240
5241    #[test]
5242    fn intersection_size_check_before_alphabet_check() {
5243        let module = make_constrained_sequence(
5244            Type::IA5String(None),
5245            SubtypeConstraint::Intersection(vec![
5246                SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
5247                    min: ConstraintValue::Integer(1),
5248                    max: ConstraintValue::Integer(64),
5249                })),
5250                SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'A', max: 'Z' }]),
5251            ]),
5252        );
5253        let result = generate_c_impl(&module, impl_config()).unwrap();
5254        let size_pos = result
5255            .find("Validate SIZE range constraint")
5256            .expect("SIZE check present");
5257        let alpha_pos = result
5258            .find("Validate FROM (permitted alphabet) constraint")
5259            .expect("alphabet check present");
5260        assert!(size_pos < alpha_pos, "SIZE check precedes alphabet check");
5261    }
5262
5263    // -----------------------------------------------------------------------
5264    // PATTERN constraint validation — Task 5
5265    // -----------------------------------------------------------------------
5266
5267    #[test]
5268    fn pattern_skip_emits_comment_not_regex() {
5269        // Skip mode (default): emit a comment, no regex call.
5270        let module = make_constrained_sequence(
5271            Type::IA5String(None),
5272            SubtypeConstraint::Pattern("[0-9]+".to_string()),
5273        );
5274        let result = generate_c_impl(&module, impl_config()).unwrap();
5275        assert!(
5276            result.contains("PATTERN constraint \"[0-9]+\" skipped"),
5277            "skip comment present"
5278        );
5279        assert!(
5280            !result.contains("regcomp") && !result.contains("pcre2_compile"),
5281            "no regex call in Skip mode"
5282        );
5283    }
5284
5285    #[test]
5286    fn pattern_posix_emits_regex_include_and_regcomp() {
5287        let module = make_constrained_sequence(
5288            Type::IA5String(None),
5289            SubtypeConstraint::Pattern("[A-Z]+".to_string()),
5290        );
5291        let config = CImplConfig {
5292            header_file: "test.h".to_string(),
5293            arena_mode: false,
5294            pattern_mode: PatternMode::Posix,
5295            with_containing: false,
5296        };
5297        let result = generate_c_impl(&module, config).unwrap();
5298        // Header include
5299        assert!(result.contains("#include <regex.h>"), "regex.h included");
5300        // Validation comment
5301        assert!(
5302            result.contains("Validate PATTERN constraint \"[A-Z]+\" (POSIX ERE)"),
5303            "posix comment present"
5304        );
5305        // POSIX API calls
5306        assert!(result.contains("regcomp("), "regcomp call present");
5307        assert!(result.contains("regexec("), "regexec call present");
5308        assert!(result.contains("regfree("), "regfree call present");
5309        // No PCRE2
5310        assert!(
5311            !result.contains("pcre2_compile"),
5312            "no pcre2 call in Posix mode"
5313        );
5314    }
5315
5316    #[test]
5317    fn pattern_pcre2_emits_pcre2_include_and_compile() {
5318        let module = make_constrained_sequence(
5319            Type::IA5String(None),
5320            SubtypeConstraint::Pattern("[0-9]{3}".to_string()),
5321        );
5322        let config = CImplConfig {
5323            header_file: "test.h".to_string(),
5324            arena_mode: false,
5325            pattern_mode: PatternMode::Pcre2,
5326            with_containing: false,
5327        };
5328        let result = generate_c_impl(&module, config).unwrap();
5329        // Header includes
5330        assert!(
5331            result.contains("#define PCRE2_CODE_UNIT_WIDTH 8"),
5332            "PCRE2 width define present"
5333        );
5334        assert!(result.contains("#include <pcre2.h>"), "pcre2.h included");
5335        // Validation comment
5336        assert!(
5337            result.contains("Validate PATTERN constraint \"[0-9]{3}\" (PCRE2)"),
5338            "pcre2 comment present"
5339        );
5340        // PCRE2 API calls
5341        assert!(result.contains("pcre2_compile("), "pcre2_compile call");
5342        assert!(result.contains("pcre2_match("), "pcre2_match call");
5343        assert!(result.contains("pcre2_code_free("), "pcre2_code_free call");
5344        // No POSIX
5345        assert!(!result.contains("regcomp"), "no regcomp in Pcre2 mode");
5346    }
5347
5348    #[test]
5349    fn pattern_non_string_type_no_validation() {
5350        // PATTERN on INTEGER is a no-op (only strings are validated)
5351        let module = make_constrained_sequence(
5352            Type::Integer(None, vec![]),
5353            SubtypeConstraint::Pattern("[0-9]+".to_string()),
5354        );
5355        let config = CImplConfig {
5356            header_file: "test.h".to_string(),
5357            arena_mode: false,
5358            pattern_mode: PatternMode::Posix,
5359            with_containing: false,
5360        };
5361        let result = generate_c_impl(&module, config).unwrap();
5362        assert!(
5363            !result.contains("regcomp") && !result.contains("pcre2_compile"),
5364            "no regex for non-string type"
5365        );
5366    }
5367
5368    #[test]
5369    fn pattern_posix_escapes_special_chars() {
5370        // Pattern with backslash: \d+ should become \\d+ in the C string literal
5371        let module = make_constrained_sequence(
5372            Type::IA5String(None),
5373            SubtypeConstraint::Pattern("\\d+".to_string()),
5374        );
5375        let config = CImplConfig {
5376            header_file: "test.h".to_string(),
5377            arena_mode: false,
5378            pattern_mode: PatternMode::Posix,
5379            with_containing: false,
5380        };
5381        let result = generate_c_impl(&module, config).unwrap();
5382        // The C string literal should have the backslash escaped
5383        assert!(
5384            result.contains("\"\\\\d+\""),
5385            "backslash escaped in C string literal"
5386        );
5387    }
5388
5389    // -----------------------------------------------------------------------
5390    // CONTAINING constraint validation — Task 6
5391    // -----------------------------------------------------------------------
5392
5393    #[test]
5394    fn containing_skip_emits_comment_not_decoder() {
5395        // Default: skip mode — emit a comment, no scratch decoder.
5396        let module = make_constrained_sequence(
5397            Type::OctetString(None),
5398            SubtypeConstraint::ContainedSubtype(Box::new(Type::TypeRef("Certificate".to_string()))),
5399        );
5400        let result = generate_c_impl(&module, impl_config()).unwrap();
5401        assert!(
5402            result.contains("CONTAINING constraint skipped"),
5403            "skip comment present"
5404        );
5405        assert!(
5406            !result.contains("synta_decoder_new"),
5407            "no decoder call in skip mode"
5408        );
5409    }
5410
5411    #[test]
5412    fn containing_enabled_emits_scratch_decoder_block() {
5413        // with_containing: true — emit full validation block.
5414        let module = make_constrained_sequence(
5415            Type::OctetString(None),
5416            SubtypeConstraint::ContainedSubtype(Box::new(Type::TypeRef("Certificate".to_string()))),
5417        );
5418        let config = CImplConfig {
5419            header_file: "test.h".to_string(),
5420            arena_mode: false,
5421            pattern_mode: PatternMode::Skip,
5422            with_containing: true,
5423        };
5424        let result = generate_c_impl(&module, config).unwrap();
5425        // Comment
5426        assert!(
5427            result.contains("Validate CONTAINING constraint (Certificate)"),
5428            "containing comment present"
5429        );
5430        // Scratch decoder
5431        assert!(
5432            result.contains("synta_decoder_new("),
5433            "synta_decoder_new called"
5434        );
5435        assert!(
5436            result.contains("certificate_decode(_inner_dec, &_inner_val)"),
5437            "inner decode called"
5438        );
5439        assert!(
5440            result.contains("synta_decoder_free(_inner_dec)"),
5441            "decoder freed"
5442        );
5443        assert!(
5444            result.contains("certificate_free(&_inner_val)"),
5445            "inner value freed"
5446        );
5447        // Error check
5448        assert!(
5449            result.contains("SyntaErrorCode_InvalidArgument"),
5450            "returns InvalidArgument on decode failure"
5451        );
5452    }
5453
5454    #[test]
5455    fn containing_builtin_inner_type_emits_comment() {
5456        // Built-in inner types (e.g. INTEGER) cannot be validated — emit comment.
5457        let module = make_constrained_sequence(
5458            Type::OctetString(None),
5459            SubtypeConstraint::ContainedSubtype(Box::new(Type::Integer(None, vec![]))),
5460        );
5461        let config = CImplConfig {
5462            header_file: "test.h".to_string(),
5463            arena_mode: false,
5464            pattern_mode: PatternMode::Skip,
5465            with_containing: true,
5466        };
5467        let result = generate_c_impl(&module, config).unwrap();
5468        assert!(
5469            result.contains("CONTAINING (built-in type): validation not generated"),
5470            "built-in comment present"
5471        );
5472        assert!(
5473            !result.contains("synta_decoder_new"),
5474            "no decoder for built-in type"
5475        );
5476    }
5477
5478    #[test]
5479    fn containing_non_byte_string_no_validation() {
5480        // CONTAINING on INTEGER (not an octet string) is a no-op.
5481        let module = make_constrained_sequence(
5482            Type::Integer(None, vec![]),
5483            SubtypeConstraint::ContainedSubtype(Box::new(Type::TypeRef("Inner".to_string()))),
5484        );
5485        let config = CImplConfig {
5486            header_file: "test.h".to_string(),
5487            arena_mode: false,
5488            pattern_mode: PatternMode::Skip,
5489            with_containing: true,
5490        };
5491        let result = generate_c_impl(&module, config).unwrap();
5492        assert!(
5493            !result.contains("synta_decoder_new"),
5494            "no containing check for non-byte-string base type"
5495        );
5496    }
5497
5498    // -----------------------------------------------------------------------
5499    // _default() implementation tests
5500    // -----------------------------------------------------------------------
5501
5502    fn make_default_seq(fields: Vec<SequenceField>) -> Module {
5503        Module {
5504            name: "TestModule".to_string(),
5505            oid: None,
5506            values: vec![],
5507            tagging_mode: None,
5508            imports: vec![],
5509            exports: vec![],
5510            definitions: vec![Definition {
5511                name: "Config".to_string(),
5512                ty: Type::Sequence(fields),
5513            }],
5514        }
5515    }
5516
5517    // -----------------------------------------------------------------------
5518    // Explicit / Implicit tag handling tests
5519    // -----------------------------------------------------------------------
5520
5521    fn make_tagged_seq(
5522        field_name: &str,
5523        class: TagClass,
5524        number: u32,
5525        tagging: Tagging,
5526        inner: Type,
5527        optional: bool,
5528    ) -> Module {
5529        Module {
5530            name: "TestModule".to_string(),
5531            oid: None,
5532            values: vec![],
5533            tagging_mode: None,
5534            imports: vec![],
5535            exports: vec![],
5536            definitions: vec![Definition {
5537                name: "Msg".to_string(),
5538                ty: Type::Sequence(vec![SequenceField {
5539                    name: field_name.to_string(),
5540                    ty: Type::Tagged {
5541                        tag: TagInfo {
5542                            class,
5543                            number,
5544                            tagging,
5545                        },
5546                        inner: Box::new(inner),
5547                    },
5548                    optional,
5549                    default: None,
5550                }]),
5551            }],
5552        }
5553    }
5554
5555    #[test]
5556    fn explicit_tag_decode_enters_constructed() {
5557        // [0] EXPLICIT INTEGER decode → synta_decoder_enter_constructed with tag [0] constructed
5558        let module = make_tagged_seq(
5559            "id",
5560            TagClass::ContextSpecific,
5561            0,
5562            Tagging::Explicit,
5563            Type::Integer(None, vec![]),
5564            false,
5565        );
5566        let result = generate_c_impl(&module, impl_config()).unwrap();
5567        assert!(
5568            result.contains("synta_decoder_enter_constructed"),
5569            "explicit tag decode must use synta_decoder_enter_constructed; got:\n{}",
5570            result
5571        );
5572        assert!(
5573            result.contains("SyntaTagClass_ContextSpecific"),
5574            "explicit tag decode must set ContextSpecific class; got:\n{}",
5575            result
5576        );
5577        assert!(
5578            result.contains("explicit_tag.number = 0"),
5579            "explicit tag decode must set tag number 0; got:\n{}",
5580            result
5581        );
5582        assert!(
5583            result.contains("explicit_tag.constructed = true"),
5584            "explicit tag must be constructed; got:\n{}",
5585            result
5586        );
5587        assert!(
5588            result.contains("synta_decode_integer"),
5589            "inner type still decoded with synta_decode_integer; got:\n{}",
5590            result
5591        );
5592    }
5593
5594    #[test]
5595    fn explicit_tag_encode_starts_constructed() {
5596        // [0] EXPLICIT INTEGER encode → synta_encoder_start_constructed + end_constructed
5597        let module = make_tagged_seq(
5598            "id",
5599            TagClass::ContextSpecific,
5600            0,
5601            Tagging::Explicit,
5602            Type::Integer(None, vec![]),
5603            false,
5604        );
5605        let result = generate_c_impl(&module, impl_config()).unwrap();
5606        assert!(
5607            result.contains("synta_encoder_start_constructed"),
5608            "explicit tag encode must use synta_encoder_start_constructed; got:\n{}",
5609            result
5610        );
5611        assert!(
5612            result.contains("synta_encoder_end_constructed"),
5613            "explicit tag encode must call synta_encoder_end_constructed; got:\n{}",
5614            result
5615        );
5616        assert!(
5617            result.contains("synta_encode_integer"),
5618            "inner type still encoded with synta_encode_integer; got:\n{}",
5619            result
5620        );
5621    }
5622
5623    #[test]
5624    fn explicit_optional_tag_peek_uses_tag_class_and_number() {
5625        // OPTIONAL [1] EXPLICIT BOOLEAN → peek_tag checks class and number 1
5626        let module = make_tagged_seq(
5627            "flag",
5628            TagClass::ContextSpecific,
5629            1,
5630            Tagging::Explicit,
5631            Type::Boolean,
5632            true,
5633        );
5634        let result = generate_c_impl(&module, impl_config()).unwrap();
5635        assert!(
5636            result.contains("synta_decoder_peek_tag"),
5637            "optional field must peek at tag; got:\n{}",
5638            result
5639        );
5640        assert!(
5641            result.contains("SyntaTagClass_ContextSpecific"),
5642            "peek must check ContextSpecific class; got:\n{}",
5643            result
5644        );
5645        assert!(
5646            result.contains("tag.number == 1"),
5647            "peek must check tag number 1; got:\n{}",
5648            result
5649        );
5650    }
5651
5652    #[test]
5653    fn implicit_tag_decode_emits_transparency_comment() {
5654        // [2] IMPLICIT OCTET STRING → fallback with comment (tag replacement not yet supported)
5655        let module = make_tagged_seq(
5656            "data",
5657            TagClass::ContextSpecific,
5658            2,
5659            Tagging::Implicit,
5660            Type::OctetString(None),
5661            false,
5662        );
5663        let result = generate_c_impl(&module, impl_config()).unwrap();
5664        assert!(
5665            result.contains("IMPLICIT"),
5666            "implicit tag decode should emit IMPLICIT comment; got:\n{}",
5667            result
5668        );
5669        // Still decodes the inner type
5670        assert!(
5671            result.contains("synta_decode_octet_string"),
5672            "implicit tag falls back to inner type decode; got:\n{}",
5673            result
5674        );
5675    }
5676
5677    #[test]
5678    fn implicit_tag_encode_emits_transparency_comment() {
5679        // [2] IMPLICIT OCTET STRING encode → fallback with comment
5680        let module = make_tagged_seq(
5681            "data",
5682            TagClass::ContextSpecific,
5683            2,
5684            Tagging::Implicit,
5685            Type::OctetString(None),
5686            false,
5687        );
5688        let result = generate_c_impl(&module, impl_config()).unwrap();
5689        assert!(
5690            result.contains("IMPLICIT"),
5691            "implicit tag encode should emit IMPLICIT comment; got:\n{}",
5692            result
5693        );
5694        // Still encodes the inner type
5695        assert!(
5696            result.contains("synta_encode_octet_string"),
5697            "implicit tag falls back to inner type encode; got:\n{}",
5698            result
5699        );
5700    }
5701
5702    #[test]
5703    fn explicit_tag_decode_frees_tagged_decoder_on_error() {
5704        // The generated code must call synta_decoder_free(tagged_decoder) on error
5705        let module = make_tagged_seq(
5706            "id",
5707            TagClass::ContextSpecific,
5708            0,
5709            Tagging::Explicit,
5710            Type::Integer(None, vec![]),
5711            false,
5712        );
5713        let result = generate_c_impl(&module, impl_config()).unwrap();
5714        // tagged_decoder must be freed on the error path
5715        assert!(
5716            result.contains("synta_decoder_free(tagged_decoder)"),
5717            "tagged_decoder must be freed; got:\n{}",
5718            result
5719        );
5720    }
5721
5722    #[test]
5723    fn default_bool_true_assigns_directly() {
5724        // BOOLEAN DEFAULT TRUE → `out.field = true;` in the generated _default().
5725        let module = make_default_seq(vec![SequenceField {
5726            name: "enabled".to_string(),
5727            ty: Type::Boolean,
5728            optional: false,
5729            default: Some("TRUE".to_string()),
5730        }]);
5731        let result = generate_c_impl(&module, impl_config()).unwrap();
5732        assert!(
5733            result.contains("Config config_default(void)"),
5734            "_default function present"
5735        );
5736        assert!(
5737            result.contains("out.enabled = true;"),
5738            "TRUE default assigned"
5739        );
5740    }
5741
5742    #[test]
5743    fn default_bool_false_no_assignment() {
5744        // BOOLEAN DEFAULT FALSE → zero-init already provides false; no explicit assignment.
5745        let module = make_default_seq(vec![SequenceField {
5746            name: "disabled".to_string(),
5747            ty: Type::Boolean,
5748            optional: false,
5749            default: Some("FALSE".to_string()),
5750        }]);
5751        let result = generate_c_impl(&module, impl_config()).unwrap();
5752        assert!(
5753            result.contains("Config config_default(void)"),
5754            "_default function present"
5755        );
5756        assert!(
5757            !result.contains("out.disabled = false;"),
5758            "no redundant false assignment"
5759        );
5760    }
5761
5762    #[test]
5763    fn default_integer_emits_comment() {
5764        // INTEGER DEFAULT → a comment; pointer type can't be trivially initialised.
5765        let module = make_default_seq(vec![SequenceField {
5766            name: "port".to_string(),
5767            ty: Type::Integer(None, vec![]),
5768            optional: false,
5769            default: Some("8080".to_string()),
5770        }]);
5771        let result = generate_c_impl(&module, impl_config()).unwrap();
5772        assert!(
5773            result.contains("/* out.port: DEFAULT 8080"),
5774            "integer default comment emitted"
5775        );
5776        assert!(
5777            !result.contains("out.port = 8080"),
5778            "no direct assignment for pointer integer"
5779        );
5780    }
5781
5782    #[test]
5783    fn default_function_not_generated_for_required_field() {
5784        // A required field with no DEFAULT means no _default() implementation.
5785        let module = make_default_seq(vec![SequenceField {
5786            name: "name".to_string(),
5787            ty: Type::Integer(None, vec![]),
5788            optional: false,
5789            default: None,
5790        }]);
5791        let result = generate_c_impl(&module, impl_config()).unwrap();
5792        assert!(
5793            !result.contains("_default(void)"),
5794            "no _default for required-field sequence"
5795        );
5796    }
5797
5798    #[test]
5799    fn default_function_for_all_optional_sequence() {
5800        // All-OPTIONAL sequence: _default() is generated with zero-init body.
5801        let module = make_default_seq(vec![
5802            SequenceField {
5803                name: "host".to_string(),
5804                ty: Type::OctetString(None),
5805                optional: true,
5806                default: None,
5807            },
5808            SequenceField {
5809                name: "port".to_string(),
5810                ty: Type::Integer(None, vec![]),
5811                optional: true,
5812                default: None,
5813            },
5814        ]);
5815        let result = generate_c_impl(&module, impl_config()).unwrap();
5816        assert!(
5817            result.contains("Config config_default(void)"),
5818            "_default function present"
5819        );
5820        assert!(result.contains("Config out = {0};"), "zero-init present");
5821        assert!(result.contains("return out;"), "returns out");
5822    }
5823}