jolt.ffi is Jolt's foreign-function interface: it loads C shared libraries and declares typed bindings over their functions, then marshals memory by hand. There is no automatic struct introspection and no garbage collection of foreign memory — you manage it, the way you would in C.
For the end-to-end guide to writing a binding — declaring the library in deps.edn, static vs dynamic linking, out-parameters, structs by offset, and binary data — see Native Interop (FFI). This page is the compact API reference for jolt.ffi itself.
(require '[jolt.ffi :as ffi])
(ffi/load-library {:darwin "libsqlite3.0.dylib" :linux "libsqlite3.so.0"})
(ffi/defcfn sqlite3-open "sqlite3_open" [:string :pointer] :int)
(let [pp (ffi/alloc (ffi/sizeof :pointer))]
(sqlite3-open "x.db" pp)
(let [db (ffi/read pp :pointer)]
(ffi/free pp)
db))
Binding functions
defcfnname csym argtypes rettype [:blocking]— define a foreign functionnamebound to the C symbolcsym.(sqlite3-open "x.db" pp)becomes an ordinary Clojure fn you call with Jolt values.foreign-fncsym argtypes rettype [:blocking]— the anonymous form; returns a callable instead ofdefing a name.- A trailing
:blockingmarks a call that may wait — network I/O, a lock, a sleep. The call is emitted collect-safe so a thread parked inside it does not pin the garbage collector. Mark anything that can block; leave pure, fast calls unmarked.(ffi/defcfn c-connect "connect" [:int :pointer :int] :int :blocking) (ffi/defcfn c-strlen "strlen" [:string] :size_t)
Calling back into Jolt
foreign-callablef argtypes rettype [:collect-safe]— wrap a Jolt fnfas a C-callable function pointer: the inverse ofdefcfn, so C can call back into Jolt (aqsortcomparator, a GTK signal handler, any C API that takes a callback). The args C passes arrive as Jolt values; the Jolt return is marshaled back perrettype. The callback stays live untilfree-callablereleases it. Pass a trailing:collect-safewhen C invokes the callback from a thread parked in a:blockingforeign call (e.g. a GUI main loop).free-callableptr— release a callable built byforeign-callable; returnsnil.export!name f argtypes rettype [:collect-safe]— publishfas a C-callable entry point undername, forjoltc build --library. An embedder resolves it viajolt_lookup("name")afterjolt_library_init. The argtypes/rettype keywords are the same asdefcfn.;; qsort comparator callable into libc (def cmp (ffi/foreign-callable (fn [pa pb] (let [a (ffi/read pa :int) b (ffi/read pb :int)] (cond (< a b) -1 (> a b) 1 :else 0))) [:pointer :pointer] :int)) (c-qsort arr n (ffi/sizeof :int) cmp) (ffi/free-callable cmp)
Types
Argument and return types are keywords:
:int:uint—int/unsigned:long:ulong—long/unsigned long:int64:uint64— 64-bit integer:size_t:ssize_t—size_t/ssize_t:iptr:uptr— pointer-sized integer (handy forNULLsentinels):double:float—double/float:char—char(a code point):uint8(alias:u8,:byte) —unsigned char, number 0–255:pointer(alias:void*) — any pointer (a machine address):string—char *, marshaled both ways (UTF-8 both directions):void— return ignored (nil)
Memory and libraries
Foreign memory is manual — allocate, use, free. There is no finalizer.
load-library[spec]— load a shared object. With no spec, binds symbols already in the running process (libc, POSIX). With a spec, a per-OS map ({:darwin "libsqlite3.0.dylib" :linux "libsqlite3.so.0"}) or a bare path. Inside adeps.ednproject you usually declare natives under:jolt/nativeinstead (see the guide).loaded?name— was a library loaded?allocnbytes— allocatenbytes; returns a pointer (address). You mustfreeit.freeptr— release memory fromalloc/string->ptr.sizeoftype— byte size of a type, for laying out structs and out-parameters.readptr type [offset]— read a typed value atptr(+ optional byte offset).writeptr type offset value— write a typed value atptr + offset.read-arrayptr n—nbytes →byte-array(binary-faithful, no encoding).write-arrayptr arr—byte-array→ memory.read-bytesptr n—nbytes → string (UTF-8).write-bytesptr s— a string's UTF-8 bytes → memory.string->ptrs— allocate a NUL-terminated C string froms(free it yourself).ptr->stringptr— read a NUL-terminated C string back.null— the null pointer;null?p— the test.