Skip to content

Jeffrey0117/modern-ffi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@modern-ffi/core

Modern FFI (Foreign Function Interface) library for Node.js - a unified replacement for ffi-napi, ref-napi, ref-struct-napi, ref-array-napi, and bindings packages.

Built on koffi for better performance, safety, and cross-platform compatibility.

Features

  • Unified API - Single package replaces 5+ legacy packages
  • Type-safe - Full TypeScript support with type inference
  • Modern - ESM and CJS support, tree-shakeable
  • Fast - Built on koffi, significantly faster than node-ffi
  • Safe - Better memory safety than legacy FFI solutions
  • Cross-platform - Windows, macOS, Linux support

Installation

npm install @modern-ffi/core

Quick Start

Loading a Dynamic Library

import { Library, types } from '@modern-ffi/core';

// Load a system library and define functions
const libm = new Library('libm', {
  ceil: [types.double, [types.double]],
  floor: [types.double, [types.double]],
  pow: [types.double, [types.double, types.double]],
});

// Call functions directly
console.log(libm.ceil(1.5));  // 2
console.log(libm.floor(1.5)); // 1
console.log(libm.pow(2, 10)); // 1024

Defining Structs

import { Struct, types } from '@modern-ffi/core';

// Define a C-style struct
const Point = new Struct({
  x: types.int,
  y: types.int,
});

// Create an instance
const p = Point.create({ x: 10, y: 20 });
console.log(p.x, p.y); // 10 20

// Nested structs
const Rect = new Struct({
  topLeft: Point,
  bottomRight: Point,
});

const rect = Rect.create({
  topLeft: { x: 0, y: 0 },
  bottomRight: { x: 100, y: 100 },
});

Using Arrays

import { FFIArray, TypedArrays, types } from '@modern-ffi/core';

// Fixed-length C array
const IntArray10 = new FFIArray(types.int, 10);
const arr = IntArray10.create([1, 2, 3, 4, 5]);

// TypedArray helpers for numeric data
const { type, create } = TypedArrays.floatArray(1024);
const floatBuffer = create(); // Float32Array

Loading Native Addons

import { bindings } from '@modern-ffi/core';

// Automatically find and load native .node addon
const myAddon = bindings('my_addon');

API Reference

Types

import { types, CType } from '@modern-ffi/core';

// Primitive types
types.void      // void
types.bool      // boolean
types.int8      // signed 8-bit
types.int16     // signed 16-bit
types.int32     // signed 32-bit
types.int64     // signed 64-bit (BigInt in JS)
types.uint8     // unsigned 8-bit
types.uint16    // unsigned 16-bit
types.uint32    // unsigned 32-bit
types.uint64    // unsigned 64-bit (BigInt in JS)
types.float     // 32-bit float
types.double    // 64-bit float

// C type aliases
types.char, types.uchar
types.short, types.ushort
types.int, types.uint
types.long, types.ulong
types.longlong, types.ulonglong
types.size_t, types.ssize_t

// Pointer and string types
types.pointer   // void*
types.string    // null-terminated string
types.cstring   // C string

Library

import { Library, createLibrary } from '@modern-ffi/core';

// Create a library wrapper
const lib = new Library('libname', {
  functionName: [returnType, [param1Type, param2Type, ...]],
});

// Or use the helper
const lib = createLibrary('libname', {
  functionName: [returnType, [paramTypes]],
});

// Call functions
const result = lib.functionName(arg1, arg2);

// Alternative: use .call()
const result = lib.call('functionName', arg1, arg2);

// Close and free resources
lib.close();

Struct

import { Struct, createStruct } from '@modern-ffi/core';

// Define a struct
const MyStruct = new Struct({
  field1: types.int,
  field2: types.double,
}, { name: 'MyStruct', pack: 1 }); // optional packing

// Properties
MyStruct.size       // total size in bytes
MyStruct.alignment  // struct alignment
MyStruct.fields     // field names array
MyStruct.offsetOf('field1') // field offset
MyStruct.typeOf('field1')   // field type

// Create instances
const instance = MyStruct.create({ field1: 42 });

FFIArray

import { FFIArray, createArray, TypedArrays } from '@modern-ffi/core';

// Create array type
const IntArray = new FFIArray(types.int, 10);

// Properties
IntArray.length      // array length
IntArray.elementType // element type
IntArray.size        // total size in bytes
IntArray.elementSize // single element size

// Create instance
const arr = IntArray.create([1, 2, 3]);

// TypedArray helpers
TypedArrays.int8Array(n)    // Int8Array
TypedArrays.uint8Array(n)   // Uint8Array
TypedArrays.int16Array(n)   // Int16Array
TypedArrays.uint16Array(n)  // Uint16Array
TypedArrays.int32Array(n)   // Int32Array
TypedArrays.uint32Array(n)  // Uint32Array
TypedArrays.int64Array(n)   // BigInt64Array
TypedArrays.uint64Array(n)  // BigUint64Array
TypedArrays.floatArray(n)   // Float32Array
TypedArrays.doubleArray(n)  // Float64Array

Bindings

import {
  bindings,
  hasBindings,
  getBindingsPath,
  getLibraryName,
  getSystemLibraryPaths,
} from '@modern-ffi/core';

// Load native addon
const addon = bindings('my_addon');
const addon = bindings({ name: 'my_addon', searchPaths: ['./lib'] });

// Check if bindings exist
if (hasBindings('my_addon')) { ... }

// Get path without loading
const path = getBindingsPath('my_addon');

// Platform helpers
getLibraryName('foo');     // 'foo.dll' / 'libfoo.dylib' / 'libfoo.so'
getSystemLibraryPaths();   // ['/usr/lib', ...] platform-specific

Callbacks

import { Library, callback, registerCallback, unregisterCallback, types } from '@modern-ffi/core';

// Define callback type
const compareCallback = callback(types.int, [types.pointer, types.pointer]);

// Register a JS function as callback
const myCompare = registerCallback(compareCallback, (a, b) => {
  // comparison logic
  return 0;
});

// Use in library call
lib.qsort(array, length, elementSize, myCompare);

// Clean up when done
unregisterCallback(myCompare);

Migration from Legacy Packages

From ffi-napi

// Before (ffi-napi)
const ffi = require('ffi-napi');
const libm = ffi.Library('libm', {
  'ceil': ['double', ['double']],
});

// After (@modern-ffi/core)
import { Library, types } from '@modern-ffi/core';
const libm = new Library('libm', {
  ceil: [types.double, [types.double]],
});

From ref-struct-napi

// Before (ref-struct-napi)
const StructType = require('ref-struct-napi');
const Point = StructType({
  x: 'int',
  y: 'int',
});

// After (@modern-ffi/core)
import { Struct, types } from '@modern-ffi/core';
const Point = new Struct({
  x: types.int,
  y: types.int,
});

From bindings

// Before (bindings)
const bindings = require('bindings');
const addon = bindings('my_addon');

// After (@modern-ffi/core)
import { bindings } from '@modern-ffi/core';
const addon = bindings('my_addon');

Platform Support

  • Windows (x64, x86)
  • macOS (x64, arm64)
  • Linux (x64, arm64, arm)

Requirements

  • Node.js >= 18.0.0

License

MIT

About

πŸ”— Modern FFI (Foreign Function Interface) - Simplified native library bindings for Node.js

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors