> bun-ffi
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.
curl "https://skillshub.wtf/secondsky/claude-skills/bun-ffi?format=md"Bun FFI
Bun's FFI allows calling native C/C++ libraries from JavaScript.
Quick Start
import { dlopen, suffix, FFIType } from "bun:ffi";
// Load library
const lib = dlopen(`libc.${suffix}`, {
printf: {
args: [FFIType.cstring],
returns: FFIType.int,
},
});
// Call function
lib.symbols.printf("Hello from C!\n");
Loading Libraries
Platform-Specific Paths
import { dlopen, suffix } from "bun:ffi";
// suffix is: "dylib" (macOS), "so" (Linux), "dll" (Windows)
// System library
const libc = dlopen(`libc.${suffix}`, { ... });
// Custom library
const myLib = dlopen(`./libmylib.${suffix}`, { ... });
// Absolute path
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 = {
// Integers
i8: FFIType.i8, // int8_t
i16: FFIType.i16, // int16_t
i32: FFIType.i32, // int32_t / int
i64: FFIType.i64, // int64_t / long long
// Unsigned integers
u8: FFIType.u8, // uint8_t
u16: FFIType.u16, // uint16_t
u32: FFIType.u32, // uint32_t
u64: FFIType.u64, // uint64_t
// Floats
f32: FFIType.f32, // float
f64: FFIType.f64, // double
// Pointers
ptr: FFIType.ptr, // void*
cstring: FFIType.cstring, // const char*
// Other
bool: FFIType.bool, // bool
void: FFIType.void, // void
};
Function Definitions
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
const lib = dlopen("./libmath.so", {
// Simple function
add: {
args: [FFIType.i32, FFIType.i32],
returns: FFIType.i32,
},
// String function
greet: {
args: [FFIType.cstring],
returns: FFIType.cstring,
},
// Pointer function
allocate: {
args: [FFIType.u64],
returns: FFIType.ptr,
},
// Void function
log_message: {
args: [FFIType.cstring],
returns: FFIType.void,
},
// No args
get_version: {
args: [],
returns: FFIType.cstring,
},
});
// Call functions
const sum = lib.symbols.add(1, 2); // 3
const message = lib.symbols.greet(ptr(Buffer.from("World\0")));
Working with Strings
import { dlopen, FFIType, ptr, CString } from "bun:ffi";
// Passing strings to C
const str = Buffer.from("Hello\0"); // Must be null-terminated
lib.symbols.print_string(ptr(str));
// Receiving strings from C
const result = lib.symbols.get_string();
const jsString = new CString(result); // Convert to JS string
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,
},
});
// Allocate buffer
const size = 1024;
const bufPtr = lib.symbols.create_buffer(size);
// Fill buffer
lib.symbols.fill_buffer(bufPtr, 0xff, size);
// Read buffer as ArrayBuffer
const arrayBuffer = toArrayBuffer(bufPtr, 0, size);
const view = new Uint8Array(arrayBuffer);
console.log(view); // [255, 255, 255, ...]
// Free buffer
lib.symbols.free_buffer(bufPtr);
Structs
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 manually
const point = new ArrayBuffer(8); // 2 x int32
const view = new DataView(point);
view.setInt32(0, 10, true); // x = 10
view.setInt32(4, 20, true); // y = 20
// Pass to C
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], // callback
returns: FFIType.void,
},
});
// Create callback
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;
}
);
// Use callback
lib.symbols.sort_array(arrayPtr, length, compareCallback.ptr);
// Close callback when done
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,
},
});
// Open database
const dbPtrArray = new BigInt64Array(1);
const dbPath = Buffer.from("test.db\0");
sqlite.symbols.sqlite3_open(ptr(dbPath), ptr(dbPtrArray));
const db = dbPtrArray[0];
// Execute query
const sql = Buffer.from("CREATE TABLE test (id INTEGER);\0");
sqlite.symbols.sqlite3_exec(db, ptr(sql), null, null, null);
// Close
sqlite.symbols.sqlite3_close(db);
Memory Management
// Manual allocation
const 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 function
lib.symbols.free(cPointer);
Thread Safety
// FFI calls are synchronous and block the main thread
// For long-running operations, use Web Workers:
// worker.ts
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
> related_skills --same-repo
> zustand-state-management
--- name: zustand-state-management description: Zustand state management for React with TypeScript. Use for global state, Redux/Context API migration, localStorage persistence, slices pattern, devtools, Next.js SSR, or encountering hydration errors, TypeScript inference issues, persist middleware problems, infinite render loops. Keywords: zustand, state management, React state, TypeScript state, persist middleware, devtools, slices pattern, global state, React hooks, create store, useBoundS
> zod
TypeScript-first schema validation and type inference. Use for validating API requests/responses, form data, env vars, configs, defining type-safe schemas with runtime validation, transforming data, generating JSON Schema for OpenAPI/AI, or encountering missing validation errors, type inference issues, validation error handling problems. Zero dependencies (2kb gzipped).
> xss-prevention
--- name: xss-prevention description: XSS attack prevention with input sanitization, output encoding, Content Security Policy. Use for user-generated content, rich text editors, web application security, or encountering stored XSS, reflected XSS, DOM manipulation, script injection errors. Keywords: sanitization, HTML-encoding, DOMPurify, CSP, Content-Security-Policy, rich-text-editor, user-input, escaping, innerHTML, DOM-manipulation, stored-XSS, reflected-XSS, input-validation, output-encodi
> wordpress-plugin-core
--- name: wordpress-plugin-core description: WordPress plugin development with hooks, security, REST API, custom post types. Use for plugin creation, $wpdb queries, Settings API, or encountering SQL injection, XSS, CSRF, nonce errors. Keywords: wordpress plugin development, wordpress security, wordpress hooks, wordpress filters, wordpress database, wpdb prepare, sanitize_text_field, esc_html, wp_nonce, custom post type, register_post_type, settings api, rest api, admin-ajax, wordpress sql inj