infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
infix: A JIT-powered FFI library for C

infix is a modern, lightweight C library that lets you call any C function or create C callbacks at runtime, using simple, human-readable strings to describe the function's signature.

It's designed to be the simplest way to add a dynamic Foreign Function Interface (FFI) to your project, whether you're building a language runtime, a plugin system, or just need to call functions from a dynamically loaded library.

![CI](https://github.com/sanko/infix/actions/workflows/ci.yml/badge.svg)

Key Features

  • Human-Readable Signatures: Describe complex C functions with an intuitive string format (e.g., "({double, double}, int) -> *char").
  • Forward & Reverse Calls: Call C functions ("forward") and create C function pointers that call back into your code ("reverse").
  • Direct Marshalling API: Build high-performance language bindings where the JIT compiler calls your object-unboxing functions directly, bypassing intermediate buffers.
  • Simple Integration: Add a single C file and a header directory to your project to get started. No complex dependencies.
  • Type Registry: Define, reuse, and link complex, recursive, and mutually-dependent structs by name.
  • Security-First Design: Hardened against vulnerabilities with Write XOR Execute (W^X) memory, guard pages, and fuzz testing.
  • High Performance: A Just-in-Time (JIT) compiler generates optimized machine code trampolines, making calls nearly as fast as a direct C call after the initial setup.

Full Documentation


How It Works: A Quick Example

The heart of infix is its signature string. Here’s how you would call a simple C function:

#include <infix/infix.h>
#include <stdio.h>
// The C function we want to call.
int add(int a, int b) { return a + b; }
int main() {
// 1. Describe the function's signature as a string.
const char* signature = "(int, int) -> int";
// 2. Create a "trampoline"—a JIT-compiled function wrapper.
infix_forward_t* trampoline = NULL;
infix_forward_create(&trampoline, signature, (void*)add, NULL);
// 3. Prepare an array of *pointers* to the arguments.
int a = 10, b = 32;
void* args[] = { &a, &b };
int result;
// 4. Call the function through the trampoline.
cif(&result, args);
printf("Result: %d\n", result); // Output: Result: 42
// 5. Clean up.
infix_forward_destroy(trampoline);
return 0;
}
void * args[]
Definition 202_in_structs.c:57
int main(void)
Definition 821_threading_bare.c:69
static int add(int a, int b)
Definition Ch04_ThreadSafety.c:25
void(* infix_cif_func)(void *, void **)
A function pointer type for a bound forward trampoline.
Definition infix.h:336
c23_nodiscard infix_status infix_forward_create(infix_forward_t **, const char *, void *, infix_registry_t *)
Creates a "bound" forward trampoline from a signature string.
Definition trampoline.c:989
c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t *)
Gets the callable function pointer from a bound forward trampoline.
Definition trampoline.c:278
void infix_forward_destroy(infix_forward_t *)
Destroys a forward trampoline and frees all associated memory.
Definition trampoline.c:631
The public interface for the infix FFI library.
Internal definition of a forward trampoline handle.
Definition infix_internals.h:88

Creating Callbacks

infix can also generate native C function pointers that call back into your code. This is perfect for interfacing with C libraries that expect callbacks, like qsort.

#include <infix/infix.h>
#include <stdlib.h>
// The C handler function for our callback.
int compare_integers(const void* a, const void* b) {
return (*(const int*)a - *(const int*)b);
}
void run_qsort_example() {
// 1. Describe the callback's signature.
const char* cmp_sig = "(*void, *void) -> int";
// 2. Create a reverse trampoline.
infix_reverse_t* context = NULL;
infix_reverse_create_callback(&context, cmp_sig, (void*)compare_integers, NULL);
// 3. Get the JIT-compiled C function pointer.
int (*my_comparator)(const void*, const void*) = infix_reverse_get_code(context);
// 4. Use the generated callback with the C library function.
int numbers[] = { 5, 1, 4, 2, 3 };
qsort(numbers, 5, sizeof(int), my_comparator);
// `numbers` is now sorted: [1, 2, 3, 4, 5]
}
c23_nodiscard infix_status infix_reverse_create_callback(infix_reverse_t **, const char *, void *, infix_registry_t *)
Creates a type-safe reverse trampoline (callback).
Definition trampoline.c:1044
c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t *)
Gets the native, callable C function pointer from a reverse trampoline.
Definition trampoline.c:895
void infix_reverse_destroy(infix_reverse_t *)
Destroys a reverse trampoline and frees all associated memory.
Definition trampoline.c:877
Internal definition of a reverse trampoline (callback/closure) handle.
Definition infix_internals.h:114

High-Performance Language Bindings

If you are writing a binding for a language like Python, Perl, or Lua, infix offers a specialized direct marshalling API. This allows the JIT compiler to call your object unboxing functions ("marshallers") directly, eliminating the need to allocate intermediate C arrays.

// A mock object from a scripting language.
typedef struct { int type; union { int i; double d; } val; } PyObject;
// A "Scalar Marshaller" converts a language object to a raw C value.
infix_direct_value_t marshal_int(void* obj_ptr) {
PyObject* obj = (PyObject*)obj_ptr;
return (infix_direct_value_t){ .i64 = obj->val.i };
}
void run_binding_example(void* target_func) {
// 1. Define handlers for the arguments.
handlers[0].scalar_marshaller = marshal_int;
handlers[1].scalar_marshaller = marshal_int;
// 2. Create an optimized trampoline.
infix_forward_t* trampoline;
infix_forward_create_direct(&trampoline, "(int, int) -> void", target_func, handlers, NULL);
// 3. Call it directly with an array of language objects.
PyObject* args[] = { py_obj1, py_obj2 };
// The JIT code calls `marshal_int` for each arg, then calls the target.
infix_forward_get_direct_code(trampoline)(NULL, (void**)args);
}
infix_direct_arg_handler_t handlers[2]
Definition 901_call_overhead.c:103
c23_nodiscard infix_status infix_forward_create_direct(infix_forward_t **out_trampoline, const char *signature, void *target_function, infix_direct_arg_handler_t *handlers, infix_registry_t *registry)
Creates a forward trampoline with direct, JIT-bound marshalling.
Definition trampoline.c:1000
c23_nodiscard infix_direct_cif_func infix_forward_get_direct_code(infix_forward_t *trampoline)
Gets the callable function pointer from a direct marshalling trampoline.
Definition trampoline.c:283
infix_marshaller_fn scalar_marshaller
For "in" parameters of a scalar type (int, float, pointer).
Definition infix.h:1356
int64_t i64
Used for all signed integer types up to 64 bits.
Definition infix.h:1302
A struct containing all the necessary handlers for a single function argument.
Definition infix.h:1354
A union to hold any primitive value returned by a scalar marshaller.
Definition infix.h:1300

Getting Started

The easiest way to use infix is to add its source directly to your project.

  1. Copy the src/ and include/ directories into your project.
  2. Add src/infix.c to your build system's list of source files.
  3. Add the include/ directory to your include paths.
  4. #include <infix/infix.h> in your code.

For more advanced build options, including building as a standalone library with CMake or xmake, see the Building and Integration Guide.

Project Philosophy

infix is built on three core principles:

  1. Security First: An FFI library with a JIT is a prime target for vulnerabilities. We defend against these with a multi-layered approach, including strict W^X memory, hardened integer arithmetic, and continuous fuzz testing.
  2. Performance by Design: FFI overhead should be minimal. infix separates the one-time generation cost from the near-zero call-time cost, making it exceptionally fast in high-performance applications when trampolines are cached.
  3. Simplicity and Portability: Platform- and ABI-specific logic is strictly isolated, making the library easy to maintain, simple to integrate, and straightforward to port to new architectures.

Platform Support

infix is rigorously tested on a wide array of operating systems, compilers, and architectures with every commit.

OS Version Architecture Compiler Status
DragonflyBSD 6.4.0 x86-64 GCC dragonflybsd/x64/gcc
FreeBSD 15.0 x86-64 GCC freebsd/x86/gcc
15.0 AArch64 GCC freebsd/a64/gcc
15.0 RISC-V64 GCC freebsd/r64/gcc
15.0 x86-64 Clang freebsd/x64/clang
15.0 AArch64 Clang freebsd/a64/clang
15.0 RISC-V64 Clang freebsd/r64/clang
macOS Sequoia AArch64 Clang macos/a64/clang
Sequoia AArch64 GCC macos/a64/gcc
NetBSD 10.1 AArch64 GCC netbsd/a64/gcc
10.1 x86-64 GCC netbsd/x64/gcc
OmniOS r151054 x86-64 GCC omnios/x64/gcc
OpenBSD 7.8 AArch64 Clang openbsd/a64/clang
7.8 AArch64 GCC openbsd/a64/gcc
7.8 x86-64 Clang openbsd/x64/clang
7.8 x86-64 Clang openbsd/x64/clang
7.8 RISC-V64 Clang openbsd/r64/clang
7.8 RISC-V64 GCC openbsd/r64/gcc
Solaris 11.4 x86-64 GCC solaris/x64/gcc
Ubuntu 24.04 AArch64 Clang ubuntu/a64/clang
24.04 AArch64 GCC ubuntu/a64/gcc
24.04 x86-64 Clang ubuntu/x64/clang
24.04 x86-64 GCC ubuntu/x64/gcc
Windows Server 2025 AArch64 Clang windows/a64/clang
Server 2025 AArch64 GCC windows/a64/gcc
Server 2025 AArch64 MSVC windows/a64/msvc
Server 2025 x86-64 Clang windows/x64/clang
Server 2025 x86-64 GCC windows/x64/gcc
Server 2025 x86-64 MSVC windows/x64/msvc

In addition to the CI platforms tested here on Github, I can verify infix builds and passes unit tests on Android/Termux.

Licenses

To maximize usability for all, infix is dual-licensed under the [Artistic License 2.0](LICENSE-A2) and the [MIT License](LICENSE-MIT). You may choose to use the code under the terms of either license.

At your discretion, all standalone documentation (.md), explanatory text, Doxygen-style documentation blocks, comments, and code examples contained within this repository may be used, modified, and distributed under the terms of the [Creative Commons Attribution 4.0 International License (CC BY 4.0)](LICENSE-CC). I encourage you to share and adapt the documentation for any purpose (generating an API reference website, creating tutorials, etc.), as long as you give appropriate credit.