<< All versions
Skill v1.0.1
currentAutomated scan100/100secondsky/claude-skills/bun-ffi
3 files
──Details
PublishedMay 24, 2026 at 03:25 PM
Content Hashsha256:ba2ce625ea474854...
Git SHA5e92b7170451
Bump Typepatch
──Files
Files (1 file, 7.2 KB)
SKILL.md7.2 KBactive
SKILL.md · 336 lines · 7.2 KB
version: "1.0.1" 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." metadata: version: "1.0.0" license: MIT
Bun FFI
Bun's FFI allows calling native C/C++ libraries from JavaScript.
Quick Start
typescript
import { dlopen, suffix, FFIType } from "bun:ffi";// Load libraryconst lib = dlopen(`libc.${suffix}`, {printf: {args: [FFIType.cstring],returns: FFIType.int,},});// Call functionlib.symbols.printf("Hello from C!\n");
Loading Libraries
Platform-Specific Paths
typescript
import { dlopen, suffix } from "bun:ffi";// suffix is: "dylib" (macOS), "so" (Linux), "dll" (Windows)// System libraryconst libc = dlopen(`libc.${suffix}`, { ... });// Custom libraryconst myLib = dlopen(`./libmylib.${suffix}`, { ... });// Absolute pathconst sqlite = dlopen("/usr/lib/libsqlite3.so", { ... });
Cross-Platform Loading
typescript
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
typescript
import { FFIType } from "bun:ffi";const types = {// Integersi8: FFIType.i8, // int8_ti16: FFIType.i16, // int16_ti32: FFIType.i32, // int32_t / inti64: FFIType.i64, // int64_t / long long// Unsigned integersu8: FFIType.u8, // uint8_tu16: FFIType.u16, // uint16_tu32: FFIType.u32, // uint32_tu64: FFIType.u64, // uint64_t// Floatsf32: FFIType.f32, // floatf64: FFIType.f64, // double// Pointersptr: FFIType.ptr, // void*cstring: FFIType.cstring, // const char*// Otherbool: FFIType.bool, // boolvoid: FFIType.void, // void};
Function Definitions
typescript
import { dlopen, FFIType, ptr, CString } from "bun:ffi";const lib = dlopen("./libmath.so", {// Simple functionadd: {args: [FFIType.i32, FFIType.i32],returns: FFIType.i32,},// String functiongreet: {args: [FFIType.cstring],returns: FFIType.cstring,},// Pointer functionallocate: {args: [FFIType.u64],returns: FFIType.ptr,},// Void functionlog_message: {args: [FFIType.cstring],returns: FFIType.void,},// No argsget_version: {args: [],returns: FFIType.cstring,},});// Call functionsconst sum = lib.symbols.add(1, 2); // 3const message = lib.symbols.greet(ptr(Buffer.from("World\0")));
Working with Strings
typescript
import { dlopen, FFIType, ptr, CString } from "bun:ffi";// Passing strings to Cconst str = Buffer.from("Hello\0"); // Must be null-terminatedlib.symbols.print_string(ptr(str));// Receiving strings from Cconst result = lib.symbols.get_string();const jsString = new CString(result); // Convert to JS stringconsole.log(jsString.toString());
Working with Pointers
typescript
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,},});// Allocate bufferconst size = 1024;const bufPtr = lib.symbols.create_buffer(size);// Fill bufferlib.symbols.fill_buffer(bufPtr, 0xff, size);// Read buffer as ArrayBufferconst arrayBuffer = toArrayBuffer(bufPtr, 0, size);const view = new Uint8Array(arrayBuffer);console.log(view); // [255, 255, 255, ...]// Free bufferlib.symbols.free_buffer(bufPtr);
Structs
typescript
import { dlopen, FFIType, ptr } from "bun:ffi";// C struct:// struct Point { int32_t x; int32_t y; };const lib = dlopen("./libgeom.so", {create_point: {args: [FFIType.i32, FFIType.i32],returns: FFIType.ptr, // Returns Point*},get_distance: {args: [FFIType.ptr, FFIType.ptr],returns: FFIType.f64,},});// Create struct manuallyconst point = new ArrayBuffer(8); // 2 x int32const view = new DataView(point);view.setInt32(0, 10, true); // x = 10view.setInt32(4, 20, true); // y = 20// Pass to Clib.symbols.get_distance(ptr(point), ptr(point));
Callbacks
typescript
import { dlopen, FFIType, callback } from "bun:ffi";const lib = dlopen("./libsort.so", {sort_array: {args: [FFIType.ptr, FFIType.u64, FFIType.ptr], // callbackreturns: FFIType.void,},});// Create callbackconst 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;});// Use callbacklib.symbols.sort_array(arrayPtr, length, compareCallback.ptr);// Close callback when donecompareCallback.close();
Example: SQLite
typescript
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,},});// Open databaseconst dbPtrArray = new BigInt64Array(1);const dbPath = Buffer.from("test.db\0");sqlite.symbols.sqlite3_open(ptr(dbPath), ptr(dbPtrArray));const db = dbPtrArray[0];// Execute queryconst sql = Buffer.from("CREATE TABLE test (id INTEGER);\0");sqlite.symbols.sqlite3_exec(db, ptr(sql), null, null, null);// Closesqlite.symbols.sqlite3_close(db);
Memory Management
typescript
// Manual allocationconst buffer = new ArrayBuffer(1024);const pointer = ptr(buffer);// Buffer stays valid as long as ArrayBuffer exists// JavaScript GC will clean up ArrayBuffer// For C-allocated memory, call C's free functionlib.symbols.free(cPointer);
Thread Safety
typescript
// FFI calls are synchronous and block the main thread// For long-running operations, use Web Workers:// worker.tsimport { 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