synta 0.2.5

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Constraints


synta-codegen can optionally generate runtime constraint checks in the `.c`
implementation file.  By default, constraints appear as comments only.

## Default (No Flags)

Value range constraints, size constraints, PATTERN constraints, and CONTAINING
constraints are emitted as comments only.  No runtime validation code is
generated.

## --with-regex (POSIX Regex)

For fields with a PATTERN constraint, the generated code uses POSIX
`regcomp()`/`regexec()` from `<regex.h>`:

- The pattern is compiled once (static variable, compiled on first call).
- Each decode call runs `regexec()` against the decoded string bytes.
- A failed match returns `SyntaErrorCode_InvalidEncoding`.

Link requirement: on most platforms `<regex.h>` is part of libc.  No extra
library is needed.

## --with-pcre (PCRE2)

For PATTERN constraints, uses PCRE2 (`<pcre2.h>`):

- The pattern is compiled once with `pcre2_compile()`.
- Each decode call runs `pcre2_match()`.
- A failed match returns `SyntaErrorCode_InvalidEncoding`.

Link requirement: `-lpcre2-8` (or the appropriate PCRE2 variant for your
character width).

## --with-containing (CONTAINING Constraint)

For fields carrying a CONTAINING constraint (indicating that the raw bytes
must be valid encodings of another type), the generated code allocates a
scratch decoder, decodes the inner type, and frees it:

```c,ignore
SyntaDecoder *scratch = synta_decoder_new(
    synta_octet_string_data(out->field),
    synta_octet_string_len(out->field),
    SyntaEncoding_Der);
if (!scratch) return SyntaErrorCode_OutOfMemory;
InnerType tmp = {0};
err = InnerType_decode(scratch, &tmp);
InnerType_free(&tmp);
synta_decoder_free(scratch);
if (err != SyntaErrorCode_Success) return err;
```

Without `--with-containing`, the check is a comment only.

## Constrained INTEGER

Constrained INTEGER types (`INTEGER (low..high)`) always generate a newtype
struct with inline validation helpers, regardless of constraint flags:

```c
/* INTEGER (0..100) */
typedef struct { uint8_t value; } Percentage;

static inline bool percentage_new(uint8_t v, Percentage* out) {
    if (!(v <= 100LL)) return false;
    out->value = v;
    return true;
}
static inline Percentage percentage_new_unchecked(uint8_t v) {
    Percentage out; out.value = v; return out;
}
static inline uint8_t percentage_get(const Percentage* self) {
    return self->value;
}
```

The range check is always performed by `percentage_new()`; use
`percentage_new_unchecked()` only when the value is already known to be valid.