Deterministic memory
two-pass answers “how much?”, while bundle answers “how can several operations share planned storage safely?”.
General-purpose allocation is convenient but poor at proving upper bounds. Axiom makes memory requirements part of APIs.
two-pass: plan, then build
1encode_json(user value) -> two_pass<bytes>:2 plan:3 RETURN json.encoded_size(value)4 build(view<u8> out):5 require out.size() >= planned_size6 json.encode_into(value, out)7 RETURN out[0..planned_size]The function returns a planned operation, not heap-owned bytes.
Planning phase.
Compute exact output size.
Build receives caller/planner-owned storage.
Capacity is a contract.
Encode into the supplied region.
Return the used range.
Why not malloc?
malloc answers “did allocation succeed right now?”. two-pass lets you ask for exact or proven upper-bound storage before execution and choose static, stack, arena, or shared memory explicitly.
bundle: plan several operations
1bundle page_build:2 html = template.render(model)3 gzip = compress.gzip(html.output)4 frame = http.make_response(gzip.output)5END page_build67main:8 const usize peak = page_build.required_memory()9 byte storage[peak]10 const auto response = page_build.run(storage)A bundle groups related operations.
HTML rendering has its own scratch lifetime.
Compression consumes its output.
HTTP framing adds another step.
The planner computes peak usage, not a naive sum.
One storage region.
Non-overlapping scratch lifetimes can reuse memory.
The important part
bundle is not just an arena allocator. The compiler sees a lifetime/interference graph. Parallel branches overlap and add to peak memory; sequential dead scratch regions can reuse the same bytes.