bun ffi

Bun's FFI allows calling native C/C++ libraries from JavaScript.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "bun ffi" with this command: npx skills add secondsky/claude-skills/secondsky-claude-skills-bun-ffi

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

tailwind-v4-shadcn

No summary provided by upstream source.

Repository SourceNeeds Review
General

aceternity-ui

No summary provided by upstream source.

Repository SourceNeeds Review
General

playwright

No summary provided by upstream source.

Repository SourceNeeds Review
General

zod

No summary provided by upstream source.

Repository SourceNeeds Review