diff --git a/core/mvcc/database/Cargo.toml b/core/mvcc/database/Cargo.toml index a39fb4069..0c29c664d 100644 --- a/core/mvcc/database/Cargo.toml +++ b/core/mvcc/database/Cargo.toml @@ -14,6 +14,8 @@ tokio-stream = { version = "0.1.12", optional = true, features = ["io-util"] } serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0.96" pin-project = "1.0.12" +tracing-subscriber = { version = "0", optional = true } +base64 = "0.21.0" [dev-dependencies] criterion = { version = "0.4", features = ["html_reports", "async", "async_futures"] } @@ -24,6 +26,9 @@ tracing-subscriber = "0" tracing-test = "0" mvcc-rs = { path = ".", features = ["tokio"] } +[lib] +crate-type = ["rlib", "cdylib", "staticlib"] + [[bench]] name = "my_benchmark" harness = false @@ -31,4 +36,5 @@ harness = false [features] default = [] full = ["tokio"] +c_bindings = ["tokio", "dep:tracing-subscriber"] tokio = ["dep:tokio", "dep:tokio-stream"] diff --git a/core/mvcc/database/src/lib.rs b/core/mvcc/database/src/lib.rs index d88011290..b671c908f 100644 --- a/core/mvcc/database/src/lib.rs +++ b/core/mvcc/database/src/lib.rs @@ -36,3 +36,85 @@ 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 + } + } + } +} diff --git a/core/mvcc/database/src/persistent_storage.rs b/core/mvcc/database/src/persistent_storage.rs index 8687c43a0..98b1fbd20 100644 --- a/core/mvcc/database/src/persistent_storage.rs +++ b/core/mvcc/database/src/persistent_storage.rs @@ -1,4 +1,4 @@ -use crate::database::{Result, Mutation}; +use crate::database::{Mutation, Result}; /// Persistent storage API for storing and retrieving transactions. /// TODO: final design in heavy progress!