ffi-adapter
    TypeScript icon, indicating that this package has built-in type declarations

    0.4.6 • Public • Published

    ffi-adapter

    NPM Version npm (tag) Build Status 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)

    • libraryFile: string - The shared library file path, which will be adapted and binding to the class.
    @LIBRARY('./libfactorial')
    class LibFactorial { /* ... */ }

    2 API(returnType?: FfiReturnType)

    • returnType: FfiReturnType - Optional. Specify the library function return type. Can be refered automatically by the TypeScript if the method return is not a Promise.

    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

    • args: any[] - The method args. Just place every args of the method, to th RETURN function.
    @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

    • Code & Docs © 2020-now Huan LI <zixia@zixia.net>
    • Code released under the Apache-2.0 License
    • Docs released under Creative Commons

    Install

    npm i ffi-adapter

    DownloadsWeekly Downloads

    15

    Version

    0.4.6

    License

    Apache-2.0

    Unpacked Size

    78 kB

    Total Files

    68

    Last publish

    Collaborators

    • zixia