Bun

module

bun:ffi

The 'bun:ffi' module enables high-performance calls to native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc).

Bun generates and just-in-time compiles C bindings that efficiently convert values between JavaScript types and native types, using embedded TinyCC (a small and fast C compiler). According to benchmarks, bun:ffi is roughly 2-6x faster than Node.js FFI via Node-API.

⚠️ Experimental — bun:ffi has known bugs and limitations, and should not be relied on in production. The most stable way to interact with native code from Bun is to write a Node-API module.

  • namespace read

    • function f32(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function f64(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function i16(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function i32(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function i64(
      ptr: Pointer,
      byteOffset?: number
      ): bigint;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function i8(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function intptr(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function ptr(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function u16(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function u32(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function u64(
      ptr: Pointer,
      byteOffset?: number
      ): bigint;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

    • function u8(
      ptr: Pointer,
      byteOffset?: number
      ): number;

      The read function behaves similarly to DataView, but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.

      @param ptr

      The memory address to read

      @param byteOffset

      bytes to skip before reading

      While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

  • enum FFIType

    • bool = 11

      Boolean value

      Must be true or false. 0 and 1 type coercion is not supported.

      In C, this corresponds to:

      bool
      _Bool
      
    • buffer = 20
    • char = 0
    • cstring = 14

      When used as a returns, this will automatically become a CString.

      When used in args it is equivalent to FFIType.pointer

    • double = 9

      Doubles are not supported yet!

    • f32 = 10

      Floats are not supported yet!

    • f64 = 9

      Doubles are not supported yet!

    • float = 10

      Floats are not supported yet!

    • i16 = 3

      16-bit signed integer

      Must be a value between -32768 and 32767

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      in16_t
      short // on arm64 & x64
      

      In JavaScript:

      var num = 0;
      
    • i32 = 5

      32-bit signed integer

      Alias of FFIType.int32_t

    • i64 = 7

      i64 is a 64-bit signed integer

      This is not implemented yet!

    • i64_fast = 15

      Attempt to coerce BigInt into a Number if it fits. This improves performance but means you might get a BigInt or you might get a number.

      In C, this always becomes int64_t

      In JavaScript, this could be number or it could be BigInt, depending on what value is passed in.

    • i8 = 1

      8-bit signed integer

      Must be a value between -127 and 127

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      signed char
      char // on x64 & aarch64 macOS
      

      In JavaScript:

      var num = 0;
      
    • int = 5

      32-bit signed integer

      The same as int in C

      int
      
    • int16_t = 3

      16-bit signed integer

      Must be a value between -32768 and 32767

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      in16_t
      short // on arm64 & x64
      

      In JavaScript:

      var num = 0;
      
    • int32_t = 5

      32-bit signed integer

    • int64_t = 7

      int64 is a 64-bit signed integer

      This is not implemented yet!

    • int8_t = 1

      8-bit signed integer

      Must be a value between -127 and 127

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      signed char
      char // on x64 & aarch64 macOS
      

      In JavaScript:

      var num = 0;
      
    • pointer = 12

      Pointer value

      alias of FFIType.ptr

    • ptr = 12

      Pointer value

      See Bun.FFI.ptr for more information

      In C:

      void*
      

      In JavaScript:

      ptr(new Uint8Array(1))
      
    • u16 = 4

      16-bit unsigned integer

      Must be a value between 0 and 65535, inclusive.

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      uint16_t
      unsigned short // on arm64 & x64
      

      In JavaScript:

      var num = 0;
      
    • u32 = 6

      32-bit unsigned integer

      Alias of FFIType.uint32_t

    • u64 = 8

      64-bit unsigned integer

      This is not implemented yet!

    • u64_fast = 16

      Attempt to coerce BigInt into a Number if it fits. This improves performance but means you might get a BigInt or you might get a number.

      In C, this always becomes uint64_t

      In JavaScript, this could be number or it could be BigInt, depending on what value is passed in.

    • u8 = 2

      8-bit unsigned integer

      Must be a value between 0 and 255

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      unsigned char
      

      In JavaScript:

      var num = 0;
      
    • uint16_t = 4

      16-bit unsigned integer

      Must be a value between 0 and 65535, inclusive.

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      uint16_t
      unsigned short // on arm64 & x64
      

      In JavaScript:

      var num = 0;
      
    • uint32_t = 6

      32-bit unsigned integer

      The same as unsigned int in C (on x64 & arm64)

      C:

      unsigned int
      

      JavaScript:

      ptr(new Uint32Array(1))
      
    • uint64_t = 8

      64-bit unsigned integer

      This is not implemented yet!

    • uint8_t = 2

      8-bit unsigned integer

      Must be a value between 0 and 255

      When passing to a FFI function (C ABI), type coercion is not performed.

      In C:

      unsigned char
      

      In JavaScript:

      var num = 0;
      
    • void = 13

      void value

      void arguments are not supported

      void return type is the default return type

      In C:

      void
      
  • class CString

    Get a string from a UTF-8 encoded C string If byteLength is not provided, the string is assumed to be null-terminated.

    var ptr = lib.symbols.getVersion();
    console.log(new CString(ptr));
    
    • byteLength?: number
    • byteOffset?: number
    • readonly length: number

      Returns the length of a String object.

    • ptr: Pointer

      The ptr to the C string

      This CString instance is a clone of the string, so it is safe to continue using this instance after the ptr has been freed.

    • [Symbol.iterator](): StringIterator<string>;

      Iterator

    • index: number
      ): undefined | string;

      Returns a new String consisting of the single UTF-16 code unit located at the specified index.

      @param index

      The zero-based index of the desired code unit. A negative index will count back from the last item.

    • pos: number
      ): string;

      Returns the character at the specified index.

      @param pos

      The zero-based index of the desired character.

    • index: number
      ): number;

      Returns the Unicode value of the character at the specified location.

      @param index

      The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.

    • pos: number
      ): undefined | number;

      Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point value of the UTF-16 encoded code point starting at the string element at position pos in the String resulting from converting this object to a String. If there is no element at that position, the result is undefined. If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos.

    • ...strings: string[]
      ): string;

      Returns a string that contains the concatenation of two or more strings.

      @param strings

      The strings to append to the end of the string.

    • searchString: string,
      endPosition?: number
      ): boolean;

      Returns true if the sequence of elements of searchString converted to a String is the same as the corresponding elements of this object (converted to a String) starting at endPosition – length(this). Otherwise returns false.

    • searchString: string,
      position?: number
      ): boolean;

      Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.

      @param searchString

      search string

      @param position

      If position is undefined, 0 is assumed, so as to search all of the String.

    • searchString: string,
      position?: number
      ): number;

      Returns the position of the first occurrence of a substring.

      @param searchString

      The substring to search for in the string

      @param position

      The index at which to begin searching the String object. If omitted, search starts at the beginning of the string.

    • isWellFormed(): boolean;

      Returns true if all leading surrogates and trailing surrogates appear paired and in order.

    • searchString: string,
      position?: number
      ): number;

      Returns the last occurrence of a substring in the string.

      @param searchString

      The substring to search for.

      @param position

      The index at which to begin searching. If omitted, the search begins at the end of the string.

    • that: string
      ): number;

      Determines whether two strings are equivalent in the current locale.

      @param that

      String to compare to target string

      that: string,
      locales?: string | string[],
      options?: CollatorOptions
      ): number;

      Determines whether two strings are equivalent in the current or specified locale.

      @param that

      String to compare to target string

      @param locales

      A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.

      @param options

      An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.

      that: string,
      locales?: LocalesArgument,
      options?: CollatorOptions
      ): number;

      Determines whether two strings are equivalent in the current or specified locale.

      @param that

      String to compare to target string

      @param locales

      A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.

      @param options

      An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.

    • regexp: string | RegExp
      ): null | RegExpMatchArray;

      Matches a string with a regular expression, and returns an array containing the results of that search.

      @param regexp

      A variable name or string literal containing the regular expression pattern and flags.

      matcher: { [match](string: string): null | RegExpMatchArray }
      ): null | RegExpMatchArray;

      Matches a string or an object that supports being matched against, and returns an array containing the results of that search, or null if no matches are found.

      @param matcher

      An object that supports being matched against.

    • regexp: RegExp
      ): RegExpStringIterator<RegExpExecArray>;

      Matches a string with a regular expression, and returns an iterable of matches containing the results of that search.

      @param regexp

      A variable name or string literal containing the regular expression pattern and flags.

    • form: 'NFC' | 'NFD' | 'NFKC' | 'NFKD'
      ): string;

      Returns the String value result of normalizing the string into the normalization form named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.

      @param form

      Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default is "NFC"

      form?: string
      ): string;

      Returns the String value result of normalizing the string into the normalization form named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.

      @param form

      Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default is "NFC"

    • maxLength: number,
      fillString?: string
      ): string;

      Pads the current string with a given string (possibly repeated) so that the resulting string reaches a given length. The padding is applied from the end (right) of the current string.

      @param maxLength

      The length of the resulting string once the current string has been padded. If this parameter is smaller than the current string's length, the current string will be returned as it is.

      @param fillString

      The string to pad the current string with. If this string is too long, it will be truncated and the left-most part will be applied. The default value for this parameter is " " (U+0020).

    • maxLength: number,
      fillString?: string
      ): string;

      Pads the current string with a given string (possibly repeated) so that the resulting string reaches a given length. The padding is applied from the start (left) of the current string.

      @param maxLength

      The length of the resulting string once the current string has been padded. If this parameter is smaller than the current string's length, the current string will be returned as it is.

      @param fillString

      The string to pad the current string with. If this string is too long, it will be truncated and the left-most part will be applied. The default value for this parameter is " " (U+0020).

    • count: number
      ): string;

      Returns a String value that is made from count copies appended together. If count is 0, the empty string is returned.

      @param count

      number of copies to append

    • searchValue: string | RegExp,
      replaceValue: string
      ): string;

      Replaces text in a string, using a regular expression or search string.

      @param searchValue

      A string or regular expression to search for.

      @param replaceValue

      A string containing the text to replace. When the searchValue is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of searchValue is replaced.

      searchValue: string | RegExp,
      replacer: (substring: string, ...args: any[]) => string
      ): string;

      Replaces text in a string, using a regular expression or search string.

      @param searchValue

      A string to search for.

      @param replacer

      A function that returns the replacement text.

      searchValue: { [replace](string: string, replaceValue: string): string },
      replaceValue: string
      ): string;

      Passes a string and replaceValue to the [Symbol.replace] method on searchValue. This method is expected to implement its own replacement algorithm.

      @param searchValue

      An object that supports searching for and replacing matches within a string.

      @param replaceValue

      The replacement text.

      searchValue: { [replace](string: string, replacer: (substring: string, ...args: any[]) => string): string },
      replacer: (substring: string, ...args: any[]) => string
      ): string;

      Replaces text in a string, using an object that supports replacement within a string.

      @param searchValue

      A object can search for and replace matches within a string.

      @param replacer

      A function that returns the replacement text.

    • searchValue: string | RegExp,
      replaceValue: string
      ): string;

      Replace all instances of a substring in a string, using a regular expression or search string.

      @param searchValue

      A string to search for.

      @param replaceValue

      A string containing the text to replace for every successful match of searchValue in this string.

      searchValue: string | RegExp,
      replacer: (substring: string, ...args: any[]) => string
      ): string;

      Replace all instances of a substring in a string, using a regular expression or search string.

      @param searchValue

      A string to search for.

      @param replacer

      A function that returns the replacement text.

    • regexp: string | RegExp
      ): number;

      Finds the first substring match in a regular expression search.

      @param regexp

      The regular expression pattern and applicable flags.

      searcher: { [search](string: string): number }
      ): number;

      Finds the first substring match in a regular expression search.

      @param searcher

      An object which supports searching within a string.

    • start?: number,
      end?: number
      ): string;

      Returns a section of a string.

      @param start

      The index to the beginning of the specified portion of stringObj.

      @param end

      The index to the end of the specified portion of stringObj. The substring includes the characters up to, but not including, the character indicated by end. If this value is not specified, the substring continues to the end of stringObj.

    • separator: string | RegExp,
      limit?: number
      ): string[];

      Split a string into substrings using the specified separator and return them as an array.

      @param separator

      A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned.

      @param limit

      A value used to limit the number of elements returned in the array.

      splitter: { [split](string: string, limit?: number): string[] },
      limit?: number
      ): string[];

      Split a string into substrings using the specified separator and return them as an array.

      @param splitter

      An object that can split a string.

      @param limit

      A value used to limit the number of elements returned in the array.

    • searchString: string,
      position?: number
      ): boolean;

      Returns true if the sequence of elements of searchString converted to a String is the same as the corresponding elements of this object (converted to a String) starting at position. Otherwise returns false.

    • start: number,
      end?: number
      ): string;

      Returns the substring at the specified location within a String object.

      @param start

      The zero-based index number indicating the beginning of the substring.

      @param end

      Zero-based index number indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end. If end is omitted, the characters from start through the end of the original string are returned.

    • locales?: string | string[]
      ): string;

      Converts all alphabetic characters to lowercase, taking into account the host environment's current locale.

      locales?: LocalesArgument
      ): string;

      Converts all alphabetic characters to lowercase, taking into account the host environment's current locale.

    • locales?: string | string[]
      ): string;

      Returns a string where all alphabetic characters have been converted to uppercase, taking into account the host environment's current locale.

      locales?: LocalesArgument
      ): string;

      Returns a string where all alphabetic characters have been converted to uppercase, taking into account the host environment's current locale.

    • toLowerCase(): string;

      Converts all the alphabetic characters in a string to lowercase.

    • toString(): string;

      Returns a string representation of a string.

    • toUpperCase(): string;

      Converts all the alphabetic characters in a string to uppercase.

    • toWellFormed(): string;

      Returns a string where all lone or out-of-order surrogates have been replaced by the Unicode replacement character (U+FFFD).

    • trim(): string;

      Removes the leading and trailing white space and line terminator characters from a string.

    • trimEnd(): string;

      Removes the trailing white space and line terminator characters from a string.

    • trimStart(): string;

      Removes the leading white space and line terminator characters from a string.

    • valueOf(): string;

      Returns the primitive value of the specified object.

    • static fromCharCode(
      ...codes: number[]
      ): string;
    • ...codePoints: number[]
      ): string;

      Return the String value whose elements are, in order, the elements in the List elements. If length is 0, the empty string is returned.

    • static raw(
      template: { raw: readonly string[] | ArrayLike<string> },
      ...substitutions: any[]
      ): string;

      String.raw is usually used as a tag function of a Tagged Template String. When called as such, the first argument will be a well formed template call site object and the rest parameter will contain the substitution values. It can also be called directly, for example, to interleave strings and values from your own tag function, and in this case the only thing it needs from the first argument is the raw property.

      @param template

      A well-formed template string call site representation.

      @param substitutions

      A set of substitution values.

  • class JSCallback

    Pass a JavaScript function to FFI (Foreign Function Interface)

    • readonly ptr: null | Pointer

      The pointer to the C function

      Becomes null once JSCallback.prototype.close is called

    • readonly threadsafe: boolean

      Can the callback be called from a different thread?

    • close(): void;

      Free the memory allocated for the callback

      If called multiple times, does nothing after the first call.

  • const FFIFunctionCallableSymbol: unique symbol
  • const suffix: string

    Platform-specific file extension name for dynamic libraries

    "." is not included

    "dylib" // macOS
    
  • function cc<Fns extends Record<string, FFIFunction>>(
    options: { define: Record<string, string>; flags: string | string[]; include: string | string[]; library: string | string[]; source: string | URL | BunFile; symbols: Fns }
    ): Library<Fns>;

    Experimental: Compile ISO C11 source code using TinyCC, and make symbols available as functions to JavaScript.

    @returns

    Library<Fns>

    Hello, World!

    JavaScript:

    import { cc } from "bun:ffi";
    import source from "./hello.c" with {type: "file"};
    const {symbols: {hello}} = cc({
      source,
      symbols: {
        hello: {
          returns: "cstring",
          args: [],
        },
      },
    });
    // "Hello, World!"
    console.log(hello());
    

    ./hello.c:

    #include <stdio.h>
    const char* hello() {
      return "Hello, World!";
    }
    
  • function CFunction(
    fn: FFIFunction & { ptr: Pointer }
    ): CallableFunction & { close(): void };

    Turn a native library's function pointer into a JavaScript function

    Libraries using Node-API & bun:ffi in the same module could use this to skip an extra dlopen() step.

    @param fn

    FFIFunction declaration. ptr is required

    import {CFunction} from 'bun:ffi';
    
    const getVersion = new CFunction({
      returns: "cstring",
      args: [],
      ptr: myNativeLibraryGetVersion,
    });
    getVersion();
    getVersion.close();
    

    This is powered by just-in-time compiling C wrappers that convert JavaScript types to C types and back. Internally, bun uses tinycc, so a big thanks goes to Fabrice Bellard and TinyCC maintainers for making this possible.

  • function dlopen<Fns extends Record<string, FFIFunction>>(
    name: string | URL | BunFile,
    symbols: Fns
    ): Library<Fns>;

    Open a library using "bun:ffi"

    @param name

    The name of the library or file path. This will be passed to dlopen()

    @param symbols

    Map of symbols to load where the key is the symbol name and the value is the FFIFunction

    import {dlopen} from 'bun:ffi';
    
    const lib = dlopen("duckdb.dylib", {
      get_version: {
        returns: "cstring",
        args: [],
      },
    });
    lib.symbols.get_version();
    // "1.0.0"
    

    This is powered by just-in-time compiling C wrappers that convert JavaScript types to C types and back. Internally, bun uses tinycc, so a big thanks goes to Fabrice Bellard and TinyCC maintainers for making this possible.

  • function linkSymbols<Fns extends Record<string, FFIFunction>>(
    symbols: Fns
    ): Library<Fns>;

    Link a map of symbols to JavaScript functions

    This lets you use native libraries that were already loaded somehow. You usually will want dlopen instead.

    You could use this with Node-API to skip loading a second time.

    @param symbols

    Map of symbols to load where the key is the symbol name and the value is the FFIFunction

    import { linkSymbols } from "bun:ffi";
    
    const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();
    
    const lib = linkSymbols({
      // Unlike with dlopen(), the names here can be whatever you want
      getMajor: {
        returns: "cstring",
        args: [],
    
        // Since this doesn't use dlsym(), you have to provide a valid ptr
        // That ptr could be a number or a bigint
        // An invalid pointer will crash your program.
        ptr: majorPtr,
      },
      getMinor: {
        returns: "cstring",
        args: [],
        ptr: minorPtr,
      },
      getPatch: {
        returns: "cstring",
        args: [],
        ptr: patchPtr,
      },
    });
    
    const [major, minor, patch] = [
      lib.symbols.getMajor(),
      lib.symbols.getMinor(),
      lib.symbols.getPatch(),
    ];
    

    This is powered by just-in-time compiling C wrappers that convert JavaScript types to C types and back. Internally, bun uses tinycc, so a big thanks goes to Fabrice Bellard and TinyCC maintainers for making this possible.

  • function ptr(
    view: ArrayBufferLike | TypedArray<ArrayBufferLike> | DataView<ArrayBufferLike>,
    byteOffset?: number

    Get the pointer backing a TypedArray or ArrayBuffer

    Use this to pass TypedArray or ArrayBuffer to C functions.

    This is for use with FFI functions. For performance reasons, FFI will not automatically convert typed arrays to C pointers.

    @param view

    the typed array or array buffer to get the pointer for

    @param byteOffset

    optional offset into the view in bytes

    From JavaScript:

    const array = new Uint8Array(10);
    const rawPtr = ptr(array);
    myFFIFunction(rawPtr);
    

    To C:

    void myFFIFunction(char* rawPtr) {
     // Do something with rawPtr
    }
    
  • function toArrayBuffer(
    ptr: Pointer,
    byteOffset?: number,
    byteLength?: number

    Read a pointer as an ArrayBuffer

    If byteLength is not provided, the pointer is assumed to be 0-terminated.

    @param ptr

    The memory address to read

    @param byteOffset

    bytes to skip before reading

    @param byteLength

    bytes to read

    While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

  • function toBuffer(
    ptr: Pointer,
    byteOffset?: number,
    byteLength?: number
    ): Buffer;

    Read a pointer as a Buffer

    If byteLength is not provided, the pointer is assumed to be 0-terminated.

    @param ptr

    The memory address to read

    @param byteOffset

    bytes to skip before reading

    @param byteLength

    bytes to read

    While there are some checks to catch invalid pointers, this is a difficult thing to do safely. Passing an invalid pointer can crash the program and reading beyond the bounds of the pointer will crash the program or cause undefined behavior. Use with care!

  • function viewSource(
    symbols: Readonly,
    is_callback?: false
    ): string[];

    View the generated C code for FFI bindings

    You probably won't need this unless there's a bug in the FFI bindings generator or you're just curious.

    function viewSource(
    callback: FFIFunction,
    is_callback: true
    ): string;

    View the generated C code for FFI bindings

    You probably won't need this unless there's a bug in the FFI bindings generator or you're just curious.

Type definitions

  • interface FFIFunction

    • readonly args?: readonly FFITypeOrString[]

      Arguments to a FFI function (C ABI)

      Defaults to an empty array, which means no arguments.

      To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see ptr.

      From JavaScript:

      import { dlopen, FFIType, suffix } from "bun:ffi"
      
      const lib = dlopen(`adder.${suffix}`, {
      	add: {
      		// FFIType can be used or you can pass string labels.
      		args: [FFIType.i32, "i32"],
      		returns: "i32",
      	},
      })
      lib.symbols.add(1, 2)
      

      In C:

      int add(int a, int b) {
        return a + b;
      }
      
    • readonly ptr?: bigint | Pointer

      Function pointer to the native function

      If provided, instead of using dlsym() to lookup the function, Bun will use this instead. This pointer should not be null (0).

      This is useful if the library has already been loaded or if the module is also using Node-API.

    • readonly returns?: FFITypeOrString

      Return type to a FFI function (C ABI)

      Defaults to FFIType.void

      To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see ptr.

      From JavaScript:

      import { dlopen, CString } from "bun:ffi"
      
      const lib = dlopen('z', {
         version: {
           returns: "ptr",
        }
      });
      console.log(new CString(lib.symbols.version()));
      

      In C:

      char* version()
      {
       return "1.0.0";
      }
      
    • readonly threadsafe?: boolean

      Can C/FFI code call this function from a separate thread?

      Only supported with JSCallback.

      This does not make the function run in a separate thread. It is still up to the application/library to run their code in a separate thread.

      By default, JSCallback calls are not thread-safe. Turning this on incurs a small performance penalty for every function call. That small performance penalty needs to be less than the performance gain from running the function in a separate thread.

  • interface FFITypeToArgsType

    • 0: number
    • 1: number
    • 10: number
    • 11: boolean
    • 12: null | TypedArray<ArrayBufferLike> | Pointer | CString
    • 13: undefined
    • 14: null | TypedArray<ArrayBufferLike> | Pointer | CString
    • 15: number | bigint
    • 16: number | bigint
    • 18: unknown
    • 19: unknown
    • 2: number
    • 20: TypedArray<ArrayBufferLike> | DataView<ArrayBufferLike>
    • 3: number
    • 4: number
    • 5: number
    • 6: number
    • 7: number | bigint
    • 8: number | bigint
    • 9: number
  • interface FFITypeToReturnsType

    • 0: number
    • 1: number
    • 10: number
    • 11: boolean
    • 12: null | Pointer
    • 13: undefined
    • 15: number | bigint
    • 16: number | bigint
    • 17: null | Pointer
    • 18: unknown
    • 19: unknown
    • 2: number
    • 20: TypedArray<ArrayBufferLike> | DataView<ArrayBufferLike>
    • 3: number
    • 4: number
    • 5: number
    • 6: number
    • 7: bigint
    • 8: bigint
    • 9: number
  • interface Library<Fns extends Symbols>

    • close(): void;

      dlclose the library, unloading the symbols and freeing allocated memory.

      Once called, the library is no longer usable.

      Calling a function from a library that has been closed is undefined behavior.

  • type ConvertFns<Fns extends Symbols> = { [K in keyof Fns]: (...args: Fns[K]['args'] extends A ? { [K in string | number | symbol]: FFITypeToArgsType[ToFFIType<A[L<L>]>] } : [unknown] extends [Fns[K]['args']] ? [] : never) => [unknown] extends [Fns[K]['returns']] ? undefined : FFITypeToReturnsType[ToFFIType<NonNullable<Fns[K]['returns']>>] }
  • type Pointer = number & { __pointer__: null }
  • type Symbols = Readonly<Record<string, FFIFunction>>
  • type ToFFIType<T extends FFITypeOrString> = T extends FFIType ? T : T extends string ? FFITypeStringToType[T] : never