Docs

Memory ownership

Own, reuse, and free Proa FFI buffers from a host runtime.

Open Markdown

The FFI boundary has two ownership modes: Proa-owned output for allocating renders, and host-owned output for _into renders. This page covers allocated output, reused buffers, JSON inputs, concurrency, and buffer sizing.

Taking ownership of allocated output

Allocating functions transfer ownership of an output buffer to the host:

examples/render.c
uint8_t *ptr = NULL;
uintptr_t len = 0;

int32_t rc = render_airbnb_home(&ptr, &len);
if (rc == 0) {
    /* ptr points to len UTF-8 bytes */
    proa_free(ptr, len);
}

Rules:

proa_free(NULL, 0) is a no-op in the current implementation. Call it only for successful allocated renders.

One allocation per render costs you an allocator round trip. The _into functions remove it.

Reusing a caller-provided buffer

_into functions let the host reuse output storage:

examples/render_into.c
uint8_t buf[256 * 1024];
uintptr_t written = 0;

int32_t rc = render_airbnb_home_into(buf, sizeof(buf), &written);
if (rc == 0) {
    /* buf[0..written] contains the HTML */
}

If the buffer is too small, retry with the required size:

examples/render_into_retry.c
uint8_t probe[1];
uintptr_t needed = 0;

int32_t rc = render_airbnb_home_into(probe, sizeof(probe), &needed);
if (rc == -1) {
    uint8_t *buf = malloc(needed);
    rc = render_airbnb_home_into(buf, needed, &needed);
    free(buf);
}

The host owns _into buffers. Do not pass them to proa_free.

Output is one direction. Props travel the other way, and they follow a third rule.

Passing JSON inputs

JSON render functions borrow the input bytes for the duration of the call:

examples/render_json.c
const uint8_t *json_ptr = ...;
uintptr_t json_len = ...;
uint8_t *out = NULL;
uintptr_t out_len = 0;

int32_t rc = render_nike_pdp_json(json_ptr, json_len, &out, &out_len);

The host keeps ownership of json_ptr. The input must remain valid until the function returns. On success, Proa owns the output, and you release it with proa_free.

Those rules hold per call. Running calls in parallel adds one more.

Calling concurrently

Use separate output buffers per concurrent call. Do not share a mutable _into buffer between requests unless the host serializes access. Allocating renders return independent buffers per successful call.

How large each of those buffers needs to be depends on the page.

Choosing buffer sizes

For the current showcase pages, a 256 KiB host buffer covers the default pages in the distributed package. For production bindings, prefer one of these patterns:

Next steps

Search

Type at least 2 characters