From 95309db710873156f3650591dd033571ee07ffca Mon Sep 17 00:00:00 2001 From: leeeon233 Date: Sat, 28 Jan 2023 19:59:37 +0800 Subject: [PATCH] feat: impl C ffi --- crates/loro-ffi/.gitignore | 2 ++ crates/loro-ffi/build.rs | 35 +++----------------- crates/loro-ffi/cbindgen.toml | 6 +++- crates/loro-ffi/examples/cpp/loro.cpp | 14 ++++++++ crates/loro-ffi/src/lib.rs | 46 ++++++++++++++++++++++++++- 5 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 crates/loro-ffi/.gitignore create mode 100644 crates/loro-ffi/examples/cpp/loro.cpp diff --git a/crates/loro-ffi/.gitignore b/crates/loro-ffi/.gitignore new file mode 100644 index 00000000..611bb729 --- /dev/null +++ b/crates/loro-ffi/.gitignore @@ -0,0 +1,2 @@ +examples/cpp/loro +examples/cpp/*.a \ No newline at end of file diff --git a/crates/loro-ffi/build.rs b/crates/loro-ffi/build.rs index 03b51f64..d8460b5c 100644 --- a/crates/loro-ffi/build.rs +++ b/crates/loro-ffi/build.rs @@ -1,35 +1,8 @@ -extern crate cbindgen; - -use cbindgen::Config; -use std::env; -use std::path::PathBuf; - fn main() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - - let package_name = env::var("CARGO_PKG_NAME").unwrap(); - let output_file = target_dir() - .join(format!("{}.hpp", package_name)) - .display() - .to_string(); - - let config = Config { - namespace: Some(String::from("loro_ffi")), - ..Default::default() - }; - + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let config = cbindgen::Config::from_file("cbindgen.toml") + .expect("Unable to find cbindgen.toml configuration file"); cbindgen::generate_with_config(&crate_dir, config) .unwrap() - .write_to_file(&output_file); -} - -/// Find the location of the `target/` directory. Note that this may be -/// overridden by `cmake`, so we also need to check the `CARGO_TARGET_DIR` -/// variable. -fn target_dir() -> PathBuf { - if let Ok(target) = env::var("CARGO_TARGET_DIR") { - PathBuf::from(target) - } else { - PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("target") - } + .write_to_file("target/loro_ffi.h"); } diff --git a/crates/loro-ffi/cbindgen.toml b/crates/loro-ffi/cbindgen.toml index 98188b59..39aae0e6 100644 --- a/crates/loro-ffi/cbindgen.toml +++ b/crates/loro-ffi/cbindgen.toml @@ -1,3 +1,7 @@ -include_guard = "libloro_ffi_h" +header = """ +typedef struct LoroCore {} LoroCore; + +typedef struct Text {} Text; +""" autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" language = "C" diff --git a/crates/loro-ffi/examples/cpp/loro.cpp b/crates/loro-ffi/examples/cpp/loro.cpp new file mode 100644 index 00000000..6e2115b6 --- /dev/null +++ b/crates/loro-ffi/examples/cpp/loro.cpp @@ -0,0 +1,14 @@ +#include +extern "C" { + #include "../../target/loro_ffi.h" +}; + +int main(void) { + LoroCore* loro = loro_new(); + Text* text = loro_get_text(loro, "text"); + text_insert(text, loro, 0, "abc"); + char* str = text_value(text); + printf("%s", str); + text_free(text); + loro_free(loro); +} diff --git a/crates/loro-ffi/src/lib.rs b/crates/loro-ffi/src/lib.rs index b2b7c9a2..d1b37f9b 100644 --- a/crates/loro-ffi/src/lib.rs +++ b/crates/loro-ffi/src/lib.rs @@ -1,4 +1,9 @@ -use loro_core::LoroCore; +use std::ffi::{c_char, c_uint, CStr, CString}; + +pub type LoroCore = loro_core::LoroCore; +pub type Text = loro_core::Text; +pub type List = loro_core::List; +pub type Map = loro_core::Map; /// create Loro with a random unique client id #[no_mangle] @@ -13,3 +18,42 @@ pub unsafe extern "C" fn loro_free(loro: *mut LoroCore) { drop(Box::from_raw(loro)); } } + +#[no_mangle] +pub unsafe extern "C" fn loro_get_text(loro: *mut LoroCore, id: *const c_char) -> *mut Text { + assert!(!loro.is_null()); + assert!(!id.is_null()); + let id = CStr::from_ptr(id).to_str().unwrap(); + let text = loro.as_mut().unwrap().get_text(id); + Box::into_raw(Box::new(text)) +} + +#[no_mangle] +pub unsafe extern "C" fn text_free(text: *mut Text) { + if !text.is_null() { + drop(Box::from_raw(text)); + } +} + +#[no_mangle] +pub unsafe extern "C" fn text_insert( + text: *mut Text, + ctx: *const LoroCore, + pos: *const c_uint, + value: *const c_char, +) { + assert!(!text.is_null()); + assert!(!ctx.is_null()); + let text = text.as_mut().unwrap(); + let ctx = ctx.as_ref().unwrap(); + let value = CStr::from_ptr(value).to_str().unwrap(); + text.insert(ctx, pos as usize, value).unwrap(); +} + +#[no_mangle] +pub unsafe extern "C" fn text_value(text: *mut Text) -> *mut c_char { + assert!(!text.is_null()); + let text = text.as_mut().unwrap(); + let value = text.get_value().as_string().unwrap().to_string(); + CString::new(value).unwrap().into_raw() +}