Basics

Guides

API Reference

Menu

Basics

Guides

API Reference

Panama Usage Guide

The panama library lets Aussom call native libraries through Java's Foreign Function and Memory API. This is useful when you want to call C ABI-compatible functions from:

  • C libraries
  • Rust libraries that export extern "C" functions
  • C++ libraries that provide extern "C" wrapper functions

panama gives you tools to:

  • open a native library
  • bind a symbol to a callable function
  • describe native function signatures
  • allocate native memory
  • copy data between Aussom and native memory
  • build and use struct layouts

Getting Started

Include the panama library:

include panama;

The most common workflow is:

  1. Open a library with panama.defaultLibrary() or panama.openLibrary()
  2. Build a function type with panama.funcType()
  3. Bind a symbol with lib.bind()
  4. Call the function with fn.call(...)

Simple example:

include panama;

lib = panama.defaultLibrary();
strlen = lib.bind("strlen", panama.funcType("long", ["cstring"]));

count = strlen.call("Hello");
sys.println("strlen = " + count);

Type Tokens

panama.funcType() and panama.field() use a small set of type tokens:

Token Meaning
bool native boolean
byte 1-byte integer
short native short
int native int
long native long-sized integer
float native float
double native double
pointer native address
cstring pointer to a null-terminated UTF-8 string
void no return value

You can also build pointer and struct types with:

  • panama.pointerTo(...)
  • panama.structLayout(...)

Default Library vs. Explicit Library

Default library

Use panama.defaultLibrary() when the function is already visible through the process default native lookup.

include panama;

lib = panama.defaultLibrary();
strlen = lib.bind("strlen", panama.funcType("long", ["cstring"]));

sys.println(strlen.call("Aussom"));

Explicit library

Use panama.openLibrary() when you want to load a specific local shared library file.

include panama;

lib = panama.openLibrary("tests/resources/panama/libpanama_fixture.so");
addFn = lib.bind("aussom_add", panama.funcType("int", ["int", "int"]));

sys.println(addFn.call(7, 8));

On Linux, a .so path is typical. On other operating systems, the filename extension will differ.

Working with Strings

For simple function calls, you can usually pass an Aussom string directly to a cstring argument:

include panama;

lib = panama.defaultLibrary();
strlen = lib.bind("strlen", panama.funcType("long", ["cstring"]));

size = strlen.call("Hello native world");
sys.println(size);

If you want explicit control over the native memory, create an arena and copy the string into it:

include panama;

lib = panama.defaultLibrary();
strlen = lib.bind("strlen", panama.funcType("long", ["cstring"]));

arenaObj = panama.arena();
strBuf = panama.cstring("Hello from an arena", arenaObj);

size = strlen.call(strBuf);
sys.println(size);

arenaObj.close();

Working with Native Buffers

NativeBuffer represents native memory with an explicit byte size.

You can allocate one from an arena:

include panama;

arenaObj = panama.arena();
buf = panama.buffer(32, arenaObj);

buf.writeInt(42);
buf.writeDouble(3.14, 8);
buf.writeString("abc");

sys.println(buf.readInt());
sys.println(buf.readDouble(8));
sys.println(buf.readString());

arenaObj.close();

Working with Aussom Buffer

Aussom's Buffer object is the normal binary-data container in the interpreter. panama supports copying bytes between Buffer and native memory.

NativeBuffer to Buffer

Use toBuffer() to copy native bytes into an Aussom Buffer:

include panama;

arenaObj = panama.arena();
buf = panama.buffer(16, arenaObj);
buf.writeString("hello");

copy = buf.toBuffer();
sys.println(copy instanceof 'Buffer');

arenaObj.close();

Buffer to NativeBuffer

Use panama.fromBuffer() to copy an Aussom Buffer into native memory owned by an arena:

include panama;

arenaObj = panama.arena();
nativeText = panama.cstring("copy me", arenaObj);

localBuffer = nativeText.toBuffer();
copyBack = panama.fromBuffer(localBuffer, arenaObj);

sys.println(copyBack.readString());

arenaObj.close();

You can also pass an Aussom Buffer directly to a function that expects a pointer or cstring. The bytes are copied into temporary native memory for that call.

Struct Layouts

Many native APIs use pointers to structs. panama lets you define a struct layout, allocate a struct instance, then read and write fields by name.

Define a struct layout

include panama;

pointLayout = panama.structLayout([
    panama.field("x", "int"),
    panama.field("y", "int")
]);

sys.println(pointLayout.size());
sys.println(pointLayout.fieldOffset("x"));
sys.println(pointLayout.fieldOffset("y"));

Allocate and use a struct

include panama;

arenaObj = panama.arena();

pointLayout = panama.structLayout([
    panama.field("x", "int"),
    panama.field("y", "int")
]);

point = arenaObj.allocLayout(pointLayout);
point.set("x", 10);
point.set("y", 20);

sys.println(point.get("x"));
sys.println(point.get("y"));

arenaObj.close();

Pass a struct pointer to a native function

This example uses the local Linux test fixture library already in this repo. The exported C function takes a struct AussomPoint* and returns the sum of its two fields.

include panama;

lib = panama.openLibrary("tests/resources/panama/libpanama_fixture.so");
arenaObj = panama.arena();

pointLayout = panama.structLayout([
    panama.field("x", "int"),
    panama.field("y", "int")
]);

sumPoint = lib.bind(
    "aussom_sum_point",
    panama.funcType("int", [panama.pointerTo(pointLayout)])
);

point = arenaObj.allocLayout(pointLayout);
point.set("x", 10);
point.set("y", 20);

sys.println(sumPoint.call(point));

arenaObj.close();

Full Example with a Local Library

This is a small complete example that loads the Linux fixture library, calls a simple add function, and then calls a struct-pointer function.

include panama;

lib = panama.openLibrary("tests/resources/panama/libpanama_fixture.so");

addFn = lib.bind("aussom_add", panama.funcType("int", ["int", "int"]));
sys.println("7 + 8 = " + addFn.call(7, 8));

arenaObj = panama.arena();

pointLayout = panama.structLayout([
    panama.field("x", "int"),
    panama.field("y", "int")
]);

sumPointFn = lib.bind(
    "aussom_sum_point",
    panama.funcType("int", [panama.pointerTo(pointLayout)])
);

point = arenaObj.allocLayout(pointLayout);
point.set("x", 25);
point.set("y", 17);

sys.println("point sum = " + sumPointFn.call(point));

arenaObj.close();

Native Arena Lifetime

NativeArena owns the memory allocated through it. When you call arenaObj.close(), that native memory should be considered invalid.

That means:

  • do not keep using NativeBuffer values after the arena closes
  • do not keep using NativeStruct values after the arena closes
  • keep the arena open for as long as native code needs the memory

Safe pattern:

arenaObj = panama.arena();

buf = panama.buffer(64, arenaObj);
// use buf here

arenaObj.close();

Tips and Limits

  • Prefer cstring for normal text arguments.
  • Prefer Buffer when you need to move raw binary data in or out of native code.
  • Use NativeStruct when a function expects a pointer to a C struct.
  • Use panama.fromBuffer() when you want a clearly owned native copy of an Aussom Buffer.
  • Native pointer return values are advanced usage. In the current API, string input and explicit memory ownership are the most fully supported paths.
  • C++ libraries should expose extern "C" wrappers instead of raw C++ function symbols.

Rebuilding the Local Test Fixture

This repo includes a small Linux fixture library for testing and examples. To rebuild it:

tests/resources/panama/build-fixture.sh

That produces:

tests/resources/panama/libpanama_fixture.so

Summary

Use panama when you need direct access to native libraries from Aussom. Start with three building blocks:

  1. NativeLibrary for symbol lookup
  2. NativeArena for owned memory
  3. NativeStructLayout / NativeStruct for structured native data

For most cases, that is enough to bind a function, pass strings or buffers, and work with struct pointers safely.