| name | Bun FFI |
| description | This skill should be used when the user asks about "bun:ffi", "foreign function interface", "calling C from Bun", "native libraries", "dlopen", "shared libraries", "calling native code", or integrating C/C++ libraries with Bun. |
| version | 1.0.0 |
Bun FFI
Bun's FFI allows calling native C/C++ libraries from JavaScript.
Quick Start
import { dlopen, suffix, FFIType } from "bun:ffi";
const lib = dlopen(`libc.${suffix}`, {
printf: {
args: [FFIType.cstring],
returns: FFIType.int,
},
});
lib.symbols.printf("Hello from C!\n");
Loading Libraries
Platform-Specific Paths
import { dlopen, suffix } from "bun:ffi";
const libc = dlopen(`libc.${suffix}`, { ... });
const myLib = dlopen(`./libmylib.${suffix}`, { ... });
const sqlite = dlopen("/usr/lib/libsqlite3.so", { ... });
Cross-Platform Loading
function getLibPath(name: string): string {
const platform = process.platform;
const paths = {
darwin: `/usr/local/lib/lib${name}.dylib`,
linux: `/usr/lib/lib${name}.so`,
win32: `C:\\Windows\\System32\\${name}.dll`,
};
return paths[platform] || paths.linux;
}
const lib = dlopen(getLibPath("mylib"), { ... });
FFI Types
import { FFIType } from "bun:ffi";
const types = {
i8: FFIType.i8,
i16: FFIType.i16,
i32: FFIType.i32,
i64: FFIType.i64,
u8: FFIType.u8,
u16: FFIType.u16,
u32: FFIType.u32,
u64: FFIType.u64,
f32: FFIType.f32,
f64: FFIType.f64,
ptr: FFIType.ptr,
cstring: FFIType.cstring,
bool: FFIType.bool,
void: FFIType.void,
};
Function Definitions
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
const lib = dlopen("./libmath.so", {
add: {
args: [FFIType.i32, FFIType.i32],
returns: FFIType.i32,
},
greet: {
args: [FFIType.cstring],
returns: FFIType.cstring,
},
allocate: {
args: [FFIType.u64],
returns: FFIType.ptr,
},
log_message: {
args: [FFIType.cstring],
returns: FFIType.void,
},
get_version: {
args: [],
returns: FFIType.cstring,
},
});
const sum = lib.symbols.add(1, 2);
const message = lib.symbols.greet(ptr(Buffer.from("World\0")));
Working with Strings
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
const str = Buffer.from("Hello\0");
lib.symbols.print_string(ptr(str));
const result = lib.symbols.get_string();
const jsString = new CString(result);
console.log(jsString.toString());
Working with Pointers
import { dlopen, FFIType, ptr, toArrayBuffer } from "bun:ffi";
const lib = dlopen("./libdata.so", {
create_buffer: {
args: [FFIType.u64],
returns: FFIType.ptr,
},
fill_buffer: {
args: [FFIType.ptr, FFIType.u8, FFIType.u64],
returns: FFIType.void,
},
free_buffer: {
args: [FFIType.ptr],
returns: FFIType.void,
},
});
const size = 1024;
const bufPtr = lib.symbols.create_buffer(size);
lib.symbols.fill_buffer(bufPtr, 0xff, size);
const arrayBuffer = toArrayBuffer(bufPtr, 0, size);
const view = new Uint8Array(arrayBuffer);
console.log(view);
lib.symbols.free_buffer(bufPtr);
Structs
import { dlopen, FFIType, ptr } from "bun:ffi";
const lib = dlopen("./libgeom.so", {
create_point: {
args: [FFIType.i32, FFIType.i32],
returns: FFIType.ptr,
},
get_distance: {
args: [FFIType.ptr, FFIType.ptr],
returns: FFIType.f64,
},
});
const point = new ArrayBuffer(8);
const view = new DataView(point);
view.setInt32(0, 10, true);
view.setInt32(4, 20, true);
lib.symbols.get_distance(ptr(point), ptr(point));
Callbacks
import { dlopen, FFIType, callback } from "bun:ffi";
const lib = dlopen("./libsort.so", {
sort_array: {
args: [FFIType.ptr, FFIType.u64, FFIType.ptr],
returns: FFIType.void,
},
});
const compareCallback = callback(
{
args: [FFIType.ptr, FFIType.ptr],
returns: FFIType.i32,
},
(a, b) => {
const aVal = new DataView(toArrayBuffer(a, 0, 4)).getInt32(0, true);
const bVal = new DataView(toArrayBuffer(b, 0, 4)).getInt32(0, true);
return aVal - bVal;
}
);
lib.symbols.sort_array(arrayPtr, length, compareCallback.ptr);
compareCallback.close();
Example: SQLite
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
const sqlite = dlopen("libsqlite3.dylib", {
sqlite3_open: {
args: [FFIType.cstring, FFIType.ptr],
returns: FFIType.i32,
},
sqlite3_exec: {
args: [FFIType.ptr, FFIType.cstring, FFIType.ptr, FFIType.ptr, FFIType.ptr],
returns: FFIType.i32,
},
sqlite3_close: {
args: [FFIType.ptr],
returns: FFIType.i32,
},
});
const dbPtrArray = new BigInt64Array(1);
const dbPath = Buffer.from("test.db\0");
sqlite.symbols.sqlite3_open(ptr(dbPath), ptr(dbPtrArray));
const db = dbPtrArray[0];
const sql = Buffer.from("CREATE TABLE test (id INTEGER);\0");
sqlite.symbols.sqlite3_exec(db, ptr(sql), null, null, null);
sqlite.symbols.sqlite3_close(db);
Memory Management
const buffer = new ArrayBuffer(1024);
const pointer = ptr(buffer);
lib.symbols.free(cPointer);
Thread Safety
import { dlopen } from "bun:ffi";
const lib = dlopen(...);
self.onmessage = (e) => {
const result = lib.symbols.expensive_operation(e.data);
self.postMessage(result);
};
Common Errors
| Error | Cause | Fix |
|---|
Library not found | Wrong path | Check library path |
Symbol not found | Wrong function name | Check function export |
Type mismatch | Wrong FFI types | Match C types exactly |
Segmentation fault | Memory error | Check pointer validity |
When to Load References
Load references/type-mappings.md when:
- Complex type conversions
- Struct layouts
- Union types
Load references/performance.md when:
- Optimizing FFI calls
- Batching operations
- Memory pooling