Skip to content
Back to skills

Clojure Coffi

ASecurity

Coffi FFI library for calling native C code from Clojure via Panama FFM API (JDK 22+). Use when: wrapping native C libraries, calling native functions, working with off-heap memory and arenas, etc.

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 8, 2026
documentationjavasqlgitapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill clojure-coffi --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Clojure Coffi?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Clojure Coffi
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-clojure-coffi/badge)](https://www.skillsdirectory.com/skills/majiayu000-clojure-coffi)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: clojure-coffi
description: |
  Coffi FFI library for calling native C code from Clojure via Panama FFM API (JDK 22+). Use when: wrapping native C libraries, calling native functions, working with off-heap memory and arenas, etc.
---

# Coffi FFI Library

Coffi wraps the Panama Foreign Function & Memory API for calling native C code from Clojure.


## Setup

Add to deps.edn:
```clojure
;; use gh cli to check latest commit with `gh browse  -c -n IGJoshua/coffi`
io.github.IGJoshua/coffi                  {:git/sha "ae3e38a449c88b998db98b0d4bffa9908dea1c79"}
```

JVM argument required:
```
--enable-native-access=ALL-UNNAMED
```

Or in deps.edn alias:
```clojure
{:aliases {:dev {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
```

Prep deps

```
clojure -Xdeps prep :aliases '[:dev :test]'
```


## Quick Start

```clojure
(require '[coffi.ffi :as ffi :refer [defcfn]]
         '[coffi.mem :as mem])

;; Wrap a native function
(defcfn strlen
  strlen [::mem/c-string] ::mem/long)

(strlen "hello") ;; => 5

;; Load a library
(ffi/load-system-library "z")        ;; System library
(ffi/load-library "path/to/lib.so")  ;; From path
```


## Core Pattern: defcfn

```clojure
(defcfn var-name
  "docstring"
  native_symbol_name [arg-types...] return-type)

;; With wrapper logic
(defcfn var-name
  "native_symbol" [arg-types...] return-type
  native-fn        ;; Binds the raw native function
  [clj-args...]    ;; Clojure argument list
  (body...))       ;; Wrapper body that calls native-fn
```


## Type Quick Reference

| Coffi Type | C Type | Notes |
|------------|--------|-------|
| `::mem/byte` | `int8_t` | |
| `::mem/short` | `int16_t` | |
| `::mem/int` | `int32_t` | |
| `::mem/long` | `int64_t` | |
| `::mem/float` | `float` | |
| `::mem/double` | `double` | |
| `::mem/pointer` | `void*` | |
| `::mem/c-string` | `char*` | Null-terminated |
| `::mem/void` | `void` | Return only |
| `[::mem/struct [...]]` | struct | See below |
| `[::mem/array type n]` | `type[n]` | Fixed size |
| `[::ffi/fn [args] ret]` | function ptr | Callbacks |

For complete type reference: [references/types.md](references/types.md)


## Struct Definition

```clojure
(require '[coffi.layout :as layout])

;; Always use layout/with-c-layout for FFI structs
(mem/defalias ::my-struct
  (layout/with-c-layout
    [::mem/struct
     [[:name ::mem/c-string]
      [:count ::mem/int]
      [:value ::mem/double]]]))

;; Use in function
(defcfn process-data
  process_data [::my-struct] ::mem/int)

(process-data {:name "test" :count 5 :value 3.14})
```


## Memory Arenas

Always use `confined-arena` with `with-open` for temporary allocations:

```clojure
(with-open [arena (mem/confined-arena)]
  (let [ptr (mem/serialize data type arena)]
    (native-fn ptr)))
;; Memory freed automatically
```

Arena types:
- `confined-arena` - Thread-local, freed on close (most common)
- `shared-arena` - Multi-thread, freed on close
- `auto-arena` - GC-managed
- `global-arena` - Never freed

For details: [references/memory.md](references/memory.md)


## Common Patterns

### Output pointer parameter

```clojure
(defcfn open-resource
  "open_resource" [::mem/c-string ::mem/pointer] ::mem/int
  native-open
  [name]
  (with-open [arena (mem/confined-arena)]
    (let [out-ptr (mem/alloc-instance ::mem/pointer arena)
          code (native-open name out-ptr)]
      (if (zero? code)
        (mem/deserialize-from out-ptr ::mem/pointer)
        (throw (ex-info "Failed" {:code code}))))))
```

### String with explicit length

```clojure
(defcfn bind-text
  "bind_text" [::mem/pointer ::mem/c-string ::mem/int] ::mem/int
  native-bind
  [handle text]
  (let [bytes (.getBytes text "UTF-8")]
    (native-bind handle text (count bytes))))
```

### Array serialization

```clojure
;; Serialize array
(mem/serialize [1 2 3 4] [::mem/array ::mem/int 4] arena)

;; For raw Java arrays (better performance)
(mem/serialize (int-array [1 2 3 4]) [::mem/array ::mem/int 4 :raw? true] arena)
```

### Callback to native code

```clojure
(defcfn set-callback
  set_callback [[::ffi/fn [::mem/int] ::mem/int]] ::mem/void)

(set-callback (fn [x] (* x 2)))
```

For more examples: [references/examples.md](references/examples.md)


## Reference Files

Read these references depending on what you are doing. You should read at least one of the now, if not all of them

- [types.md](references/types.md) - Complete type system (primitives, structs, arrays, enums, unions, custom types)
- [memory.md](references/memory.md) - Memory management (arenas, allocation, serialization, pointer ops)
- [examples.md](references/examples.md) - Real-world patterns from sqlite4clj and coffi tests

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.