Claude-skill-registry clojure-coffi
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/clojure-coffi" ~/.claude/skills/majiayu000-claude-skill-registry-clojure-coffi && rm -rf "$T"
manifest:
skills/data/clojure-coffi/SKILL.mdsource content
Coffi FFI Library
Coffi wraps the Panama Foreign Function & Memory API for calling native C code from Clojure.
Setup
Add to deps.edn:
;; 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:
{:aliases {:dev {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
Prep deps
clojure -Xdeps prep :aliases '[:dev :test]'
Quick Start
(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
(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 |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | Null-terminated |
| | Return only |
| struct | See below |
| | Fixed size |
| function ptr | Callbacks |
For complete type reference: references/types.md
Struct Definition
(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:
(with-open [arena (mem/confined-arena)] (let [ptr (mem/serialize data type arena)] (native-fn ptr))) ;; Memory freed automatically
Arena types:
- Thread-local, freed on close (most common)confined-arena
- Multi-thread, freed on closeshared-arena
- GC-managedauto-arena
- Never freedglobal-arena
For details: references/memory.md
Common Patterns
Output pointer parameter
(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
(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
;; 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
(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
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 - Complete type system (primitives, structs, arrays, enums, unions, custom types)
- memory.md - Memory management (arenas, allocation, serialization, pointer ops)
- examples.md - Real-world patterns from sqlite4clj and coffi tests