Calling Rust from Common Lisp
I didn't do anything fancy. I create C compatible wrapper in Rust, and call the wrapper from Common Lisp using CFFI.
My C compatible wrapper functions
#[no_mangle]
pub extern "C" fn zstd_line_read_new<'a>(zstd_file_path: *const c_char) -> *mut c_void {
let r_zstd_file_path = unsafe { CStr::from_ptr(zstd_file_path) };
let file = File::open(r_zstd_file_path.to_str().unwrap());
if file.is_err() {
eprintln!("Cannot open file {}", r_zstd_file_path.to_str().unwrap());
return ptr::null::<c_void>() as *mut c_void;
}
let file = file.unwrap();
let wrapper = DecoderWrapper::new(file);
Box::into_raw(Box::new(wrapper)) as *mut c_void
}
#[no_mangle]
pub extern "C" fn zstd_line_read<'a>(reader: *mut c_void) -> *const c_char {
let wrapper: *mut DecoderWrapper<'a> = reader as *mut DecoderWrapper<'a>;
let mut line = Vec::with_capacity(BUF_SIZE);
unsafe {
match (*wrapper).read_line(&mut line) {
Ok(len) => {
if len == 0 {
return ptr::null();
} else {
return CString::from_vec_unchecked(line).into_raw();
}
}
Err(e) => {
panic!(e)
}
}
}
}
I added this to Carto.toml
[lib]
crate-type = ["cdylib"]
It makes Cargo create DLL on Windows or .so on Linux instead of Rust specific library format.
In my Common Lisp code:
(ql:quickload :cffi)
(defpackage :t1
(:use :cl :cffi))
(in-package :t1)
(define-foreign-library zstd_read_line
(:win32 (:default "./target/release/zstd_read_line"))
(t (:default "./target/release/libzstd_read_line")))
(use-foreign-library zstd_read_line)
(defcfun ("zstd_line_read_new" create-zstd-reader) :pointer (zstd_archive_path :string))
(defcfun ("zstd_line_read" zstd-read-line) :string (reader :pointer))
(let ((reader (create-zstd-reader "test1.txt.zst")))
(loop for line = (zstd-read-line reader)
while line
do (print line)))
It required Quicklisp for install CFFI. This process doesn't require cbindgen because I define C interface in Common Lisp manually, as you may see at defcfun.
And then it works even on Windows 10.