diff --git a/core/mvcc/Cargo.toml b/core/mvcc/Cargo.toml index ba0f2a813..c9e8d84ba 100644 --- a/core/mvcc/Cargo.toml +++ b/core/mvcc/Cargo.toml @@ -2,9 +2,10 @@ resolver = "2" members = [ "database", + "bindings/c", ] [profile.release] codegen-units = 1 panic = "abort" -strip = true +strip = true \ No newline at end of file diff --git a/core/mvcc/bindings/c/Cargo.toml b/core/mvcc/bindings/c/Cargo.toml new file mode 100644 index 000000000..a54329eca --- /dev/null +++ b/core/mvcc/bindings/c/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "mvcc-c" +version = "0.0.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +doc = false + +[build-dependencies] +cbindgen = "0.24.0" + +[dependencies] +base64 = "0.21.0" +mvcc-rs = { path = "../../database", features = ["tokio"] } +tokio = { version = "1.27.0", features = ["full"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0" } diff --git a/core/mvcc/bindings/c/src/lib.rs b/core/mvcc/bindings/c/src/lib.rs new file mode 100644 index 000000000..e0cc44fb4 --- /dev/null +++ b/core/mvcc/bindings/c/src/lib.rs @@ -0,0 +1,79 @@ +use mvcc_rs::*; + +type Clock = clock::LocalClock; +type Storage = persistent_storage::JsonOnDisk; +type Inner = database::DatabaseInner; +type Db = database::Database>; + +static INIT_RUST_LOG: std::sync::Once = std::sync::Once::new(); + +#[repr(C)] +pub struct DbContext { + db: Db, + runtime: tokio::runtime::Runtime, +} + +#[no_mangle] +pub extern "C" fn mvccrs_new_database(path: *const std::ffi::c_char) -> *mut DbContext { + INIT_RUST_LOG.call_once(|| { + tracing_subscriber::fmt::init(); + }); + + tracing::debug!("mvccrs_new_database"); + + let clock = clock::LocalClock::new(); + let path = unsafe { std::ffi::CStr::from_ptr(path) }; + let path = match path.to_str() { + Ok(path) => path, + Err(_) => { + tracing::error!("Invalid UTF-8 path"); + return std::ptr::null_mut(); + } + }; + tracing::debug!("mvccrs: opening persistent storage at {path}"); + let storage = crate::persistent_storage::JsonOnDisk::new(path); + let db = Db::new(clock, storage); + let runtime = tokio::runtime::Runtime::new().unwrap(); + Box::into_raw(Box::new(DbContext { db, runtime })) +} + +#[no_mangle] +pub unsafe extern "C" fn mvccrs_free_database(db: *mut Db) { + tracing::debug!("mvccrs_free_database"); + let _ = Box::from_raw(db); +} + +#[no_mangle] +pub unsafe extern "C" fn mvccrs_insert( + db: *mut DbContext, + id: u64, + value_ptr: *const u8, + value_len: usize, +) -> i32 { + let value = std::slice::from_raw_parts(value_ptr, value_len); + let data = match std::str::from_utf8(value) { + Ok(value) => value.to_string(), + Err(_) => { + tracing::info!("Invalid UTF-8, let's base64 this fellow"); + use base64::{engine::general_purpose, Engine as _}; + general_purpose::STANDARD.encode(value) + } + }; + let DbContext { db, runtime } = unsafe { &mut *db }; + let row = database::Row { id, data }; + tracing::debug!("mvccrs_insert: {row:?}"); + match runtime.block_on(async move { + let tx = db.begin_tx().await; + db.insert(tx, row).await?; + db.commit_tx(tx).await + }) { + Ok(_) => { + tracing::debug!("mvccrs_insert: success"); + 0 // SQLITE_OK + } + Err(e) => { + tracing::error!("mvccrs_insert: {e}"); + 778 // SQLITE_IOERR_WRITE + } + } +} diff --git a/core/mvcc/database/src/lib.rs b/core/mvcc/database/src/lib.rs index b671c908f..b14e27be3 100644 --- a/core/mvcc/database/src/lib.rs +++ b/core/mvcc/database/src/lib.rs @@ -35,86 +35,4 @@ pub mod clock; pub mod database; pub mod errors; pub mod persistent_storage; -pub mod sync; - -#[cfg(feature = "c_bindings")] -mod c_bindings { - use super::*; - type Clock = clock::LocalClock; - type Storage = persistent_storage::JsonOnDisk; - type Inner = database::DatabaseInner; - type Db = database::Database>; - - static INIT_RUST_LOG: std::sync::Once = std::sync::Once::new(); - - #[repr(C)] - pub struct DbContext { - db: Db, - runtime: tokio::runtime::Runtime, - } - - #[no_mangle] - pub extern "C" fn mvccrs_new_database(path: *const std::ffi::c_char) -> *mut DbContext { - INIT_RUST_LOG.call_once(|| { - tracing_subscriber::fmt::init(); - }); - - tracing::debug!("mvccrs_new_database"); - - let clock = clock::LocalClock::new(); - let path = unsafe { std::ffi::CStr::from_ptr(path) }; - let path = match path.to_str() { - Ok(path) => path, - Err(_) => { - tracing::error!("Invalid UTF-8 path"); - return std::ptr::null_mut(); - } - }; - tracing::debug!("mvccrs: opening persistent storage at {path}"); - let storage = crate::persistent_storage::JsonOnDisk::new(path); - let db = Db::new(clock, storage); - let runtime = tokio::runtime::Runtime::new().unwrap(); - Box::into_raw(Box::new(DbContext { db, runtime })) - } - - #[no_mangle] - pub unsafe extern "C" fn mvccrs_free_database(db: *mut Db) { - tracing::debug!("mvccrs_free_database"); - let _ = Box::from_raw(db); - } - - #[no_mangle] - pub unsafe extern "C" fn mvccrs_insert( - db: *mut DbContext, - id: u64, - value_ptr: *const u8, - value_len: usize, - ) -> i32 { - let value = std::slice::from_raw_parts(value_ptr, value_len); - let data = match std::str::from_utf8(value) { - Ok(value) => value.to_string(), - Err(_) => { - tracing::info!("Invalid UTF-8, let's base64 this fellow"); - use base64::{engine::general_purpose, Engine as _}; - general_purpose::STANDARD.encode(value) - } - }; - let DbContext { db, runtime } = unsafe { &mut *db }; - let row = database::Row { id, data }; - tracing::debug!("mvccrs_insert: {row:?}"); - match runtime.block_on(async move { - let tx = db.begin_tx().await; - db.insert(tx, row).await?; - db.commit_tx(tx).await - }) { - Ok(_) => { - tracing::debug!("mvccrs_insert: success"); - 0 // SQLITE_OK - } - Err(e) => { - tracing::error!("mvccrs_insert: {e}"); - 778 // SQLITE_IOERR_WRITE - } - } - } -} +pub mod sync; \ No newline at end of file