# Reusable C interoperability

[العربية](guide.ar.md) · [HTTP guide](../http/guide.en.md)

## Ordinary unsafe signatures

[ordinary.aspl](../examples/c-interop/en/ordinary.aspl) calls libc `abs` with
`i32 -> i32`, and `strlen` with a copied C-string loan and `u64` size result.
The Arabic [ordinary.aspl](../examples/c-interop/ar/ordinary.aspl) uses the
same user names, library/symbol names and strings.

```sh
aspl check docs/examples/c-interop/en/ordinary.aspl
aspl run docs/examples/c-interop/en/ordinary.aspl
aspl build -o build/http-guide/ordinary docs/examples/c-interop/en/ordinary.aspl
./build/http-guide/ordinary
```

Create the output directory first. Select 0.5.0 and the supported x86-64 System V
Linux ABI. The contextual tag `c` / `سي` derives the exact signature from source
types. Mark both the foreign declaration and each call `unsafe` / `غير_آمن`.
Generic signatures admit 0..32 lowered arguments. Bool maps to one-byte C bool;
fixed-width signed/unsigned integers and f32/f64 keep their widths, c_ptr is an
opaque nullable unowned pointer, and result-only unit means C void. On this
target C char is signed, short is 16 bits, int 32, long/long long/pointers/size_t
64. Choose the real underlying type for typedefs/enums. There is no header or
symbol-based ABI inference, implicit scalar conversion, errno/result conversion,
variadic call, aggregate-by-value, long double, complex or function pointer.

`cstring(text)` / `نص_سي(text)` creates a call-scoped NUL-terminated copy.
Embedded NUL traps before C entry. C must not retain the pointer. The original
adapters `c_int__const_char_ptr` / `سي_صحيح__مؤشر_محرف_ثابت` and
`c_double__double` / `سي_مزدوج__مزدوج` remain available with their old conversions,
respectively string to i64 and f64 to f64. They do not widen generic signatures.

Runtime prepares exact literal shared libraries with RTLD_NOW | RTLD_LOCAL and
resolves each symbol in its library before entry, including unused declarations.
Missing dependencies fail loading before source effects. `check` and `build`
do not load them. The program author must match the real prototype and ensure
pointer validity and C behavior. Ordinary calls are synchronous and may block;
they are excluded transitively from HTTP handlers and managed callbacks.

## Copied Buffer values and shared native owners

Import `core::c` or `أساسي::سي`. Only its written prefix is active. Members are
`Buffer` / `مخزن`, `buffer` / `أنشئ_مخزن`, `zero_buffer` / `أنشئ_مخزن_صفري`,
`capacity` / `سعة`, `bytes` / `انسخ_بايتات`, `close` / `أغلق`.

| Function | Signature |
| --- | --- |
| buffer | `(list<u8>) -> result<Buffer>` |
| zero_buffer | `(u64) -> result<Buffer>` |
| capacity | `(Buffer) -> result<u64>` |
| bytes | `(Buffer, u64) -> result<list<u8>>` |
| close | `(Buffer) -> result<unit>` |

[buffer.لبات](../examples/c-interop/en/buffer.لبات) constructs `00 FF 41`,
changes the original list, copies out bytes, then calls libc `memset` with an
explicit output span. Both original source and extracted bytes stay independent.
The [Arabic pair](../examples/c-interop/ar/buffer.لبات) demonstrates the same rules.

```sh
aspl check docs/examples/c-interop/en/buffer.لبات
aspl run docs/examples/c-interop/en/buffer.لبات
aspl check docs/examples/c-interop/ar/buffer.لبات
aspl run docs/examples/c-interop/ar/buffer.لبات
```

`input(buffer,count)` / `إدخال`, `output` / `إخراج`, and
`inout` / `إدخال_وإخراج` annotate byte spans. Count is a nonnegative i64/u64
parameter or literal within capacity, and descriptors add no native arguments.
Output zeroes the selected span before entry; inout preserves input. A C negative
return does not roll back written bytes. Zero-length spans use a valid sentinel.
Pre-entry invalid count, closed Buffer or overlapping writable aliases trap;
read-only aliases are permitted. This does not prove that C respects the span.

Buffer copies retain one native owner, not copied mutable native storage.
Closing one alias is idempotent and invalidates all aliases. The example checks
recoverable `foreign.invalid_argument` for an oversized extraction and
`foreign.closed` for capacity after alias close. Utility admission can refuse
with `foreign.resource_limit`; it does not make ordinary C calls recoverable.
In-flight native access retains charged storage through quiescence. Last-reference
and execution cleanup release owners, including unreachable ownership cycles.
No raw-address conversion exposes a managed owner's storage.

## Reviewed managed callbacks

A managed operation needs a reviewed toolchain catalog binding with bounded
admission, accounting, cancellation and cleanup. A source C signature or safety
claim cannot register one. No independently installed managed-binding ABI is
public. The production catalog currently exposes the supplied HTTP API; the
following reusable callback demonstration is contributor-only, with its own
fixture catalog. It is deliberately refused by the production tool.

[managed.aspl](../examples/c-interop/en/managed.aspl) and its
[Arabic pair](../examples/c-interop/ar/managed.aspl) use the exact
`core::managed_fixture` descriptor in both vocabularies. The contributor fixture
has no Arabic descriptor/member aliases. The callback `respond` is a top-level
`fn(Data, list<u8>) -> list<u8>` with an explicit copied data snapshot. It receives
7 and 9 as typed values, fills the bounded event queue, checks
`foreign.overloaded`, closes its owner, then checks `foreign.closed`.

```sh
make build/aspl-managed-test
build/aspl-managed-test check docs/examples/c-interop/en/managed.aspl
build/aspl-managed-test run docs/examples/c-interop/en/managed.aspl
build/aspl-managed-test check docs/examples/c-interop/ar/managed.aspl
build/aspl-managed-test run docs/examples/c-interop/ar/managed.aspl
```

Use this fixture only in a contributor checkout. The registration owns a target
and typed data with no captures or exposed raw function pointer. Standalone
registration data must be storable and cannot contain actual Context
at any depth. Ordinary callable descriptors may occur in this data and remain
subject to transitive callback safety analysis; their representations never cross
into C. When a registration is stored in HTTP configuration/state, the HTTP C/S
rules additionally exclude exposed callable values throughout its data.
Handles inside snapshots share native owners. Native callback entry copies events
into a bounded queue and never enters the VM recursively or from native threads.
ASPL callbacks execute serially and may suspend only through admitted managed
operations. The fixture caps owners/registrations/queued events at 64, active
callbacks at 32, event/reply bytes at 4096, event time at 30000 ms and cleanup
at 1000 ms. Generic reviewed managed durations admit 1..86400000 ms; HTTP's
cleanup minimum is separately 1000 ms.

Registration/owner generations reject stale events; replies complete once.
Unregister revokes admission, waits for native quiescence and only then releases
retained data/storage. Closing an alias invalidates shared owner access.
Expected managed refusal uses `foreign.invalid_argument`, `foreign.closed`,
`foreign.resource_limit`, `foreign.overloaded`, `foreign.timeout`,
`foreign.cancelled` or `foreign.host_failure`. Parent cancellation does not
promise a failed result to a cancelled callback. Immediate ASPL-computed C returns
are excluded; a native synchronous callback that would wait for the currently
blocked ASPL execution is refused before deadlock. Ordinary unsafe calls acquire
none of these privileges.

Run `make test-http-documentation` to check both guide vocabularies, production
refusal of the fixture, and direct/source-free generated behavior. Existing
`make test-native-foreign test-managed` retains the broader signature, owner,
queue, generation and teardown checks.
