Home

Awesome

ffi-adapter

NPMNPM Version npm (tag) TypeScript

Rainbow Colors Electronic

Image suorce: Rainbow Colors Electronic

Foreign Function Interface Adapter Powered by Decorator & TypeScript

Features

  1. Binding shared library(.dll/.so/.dylib) to a TypeScript class by decorators.
  2. Support async mode when a class method defined with a return type of Promise.
  3. Supports Windows(.dll), Linux(.so), and MacOS(.dylib).
  4. The class will be forced singleton.

Requirement

  1. Node.js v10 or v11 for ffi (neither v12 nor v13, see #554)
  2. TypeScript with --target ES5, --experimentalDecorators, and --emitDecoratorMetadata options on.

Example

Background:

  1. We have a shared library file libfactorial.so (.dll/.dylib as well)
  2. the library has a function uint64_t factorial(int)
  3. We want to use factorial() in TypeScript.

Talk is cheap, show me the code

import {
  LIBRARY,
  API,
  RETURN,
}             from 'ffi-adapter'

@LIBRARY('./libfactorial')
export class LibFactorial {
  @API() factorial (n: number): number { return RETURN(n) }
}

const lib = new LibFactorial()
console.log('factorial(5) =', lib.factorial(5))
// Output: factorial(5) = 120

That's it! Use it is that easy!

Reference

import {
  LIBRARY,
  API,
  RETURN,
}           from 'ffi-adapter'

All you need is the above two decorators and one function:

  1. LIBRARY(libraryFile: string) - Class decorator
  2. API(returnType?: FfiReturnType) - Method decorator
  3. RETURN(...args: any[]) - Method need to return this function to confirm the adapting.

1 LIBRARY(libraryFile: string)

@LIBRARY('./libfactorial')
class LibFactorial { /* ... */ }

2 API(returnType?: FfiReturnType)

Specific the library function return type, and bind the same name function from library to the decorated class method.

@API('uint64') factorial(n: number): Promise<number> { ... }

3 RETURN(...args: any[]): any

@API() factorial(...args: any[]) { return RETURN(...args) }

The actual return value will be take care by the @API decorator.

Tutorial

Credit: https://github.com/node-ffi/node-ffi/tree/master/example/factorial

1 For the belowing C source code: factorial.c

#include <stdint.h>

#if defined(WIN32) || defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

EXPORT uint64_t factorial(int max) {
  int i = max;
  uint64_t result = 1;

  while (i >= 2) {
    result *= i--;
  }

  return result;
}

2 We can build a shared library libfactorial based on it

2.1 To compile libfactorial.dylib on OS X

gcc -dynamiclib -undefined suppress -flat_namespace factorial.c -o libfactorial.dylib

2.2 To compile libfactorial.so on Linux/Solaris/etc

gcc -shared -fpic factorial.c -o libfactorial.so

2.3 To compile libfactorial.dll on Windows (http://stackoverflow.com/a/2220213)

cl.exe /D_USRDLL /D_WINDLL factorial.c /link /DLL /OUT:libfactorial.dll

3 We can adapte the shared library into TypeScript as a Class

Save the following code to file lib-factorial.ts:

import {
  LIBRARY,
  API,
  RETURN,
}             from 'ffi-adapter'

@LIBRARY('./libfactorial')
export class LibFactorial {

  @API() factorial (n: number): number { return RETURN(n) }

}

4 Let's use it

import { LibFactorial } from './lib-factorial.ts'

const lib = new LibFactorial()
console.log('factorial(5) =', lib.factorial(5))
// Output: factorial(5) = 120

You will agree with me that it's super clean, beautiful, and easy to maintain! ;-)

Similiar Projects

Resources

Knowned Issues

  1. Node.js v12/13 is not supported yet. (see #554) ffi-adapter@0.4 solve this problem. (#9)

History

master

v0.4 (Aug 6, 2021)

Support all Node.js versions (10/11/12/13/14/15/16) now!

  1. Upgrade dependency from ffi to ffi-napi for supporting all newer Node.js versions with N-API. (#9)

v0.2 (Feb 4, 2020)

The first version.

  1. Use @LIBRARY(), @API(), and RETURN() as decorators to bind a shared library to a TypeScript Class.

Author

Huan LI (李卓桓) zixia@zixia.net

Profile of Huan LI (李卓桓) on StackOverflow

Copyright & License