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