From f7e36925c566cd8336e71eb507c8a766240f03a0 Mon Sep 17 00:00:00 2001 From: Weicao-CatilGrass <1992414357@qq.com> Date: Mon, 9 Mar 2026 19:42:12 +0800 Subject: Add C FFI bindings for library usage --- Cargo.lock | 8 + Cargo.toml | 28 ++ build.rs | 78 ++++- cbindgen.toml | 45 +++ ffi/Cargo.lock | 823 +++++++++++++++++++++++++++++++++++++++++++++ ffi/Cargo.toml | 12 + ffi/src/lib.rs | 12 + src/chunker/context.rs | 13 +- src/chunker/context/ffi.rs | 795 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 + 10 files changed, 1803 insertions(+), 17 deletions(-) create mode 100644 cbindgen.toml create mode 100644 ffi/Cargo.lock create mode 100644 ffi/Cargo.toml create mode 100644 ffi/src/lib.rs create mode 100644 src/chunker/context/ffi.rs diff --git a/Cargo.lock b/Cargo.lock index eb87eab..c97d8e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,14 @@ dependencies = [ "toml", ] +[[package]] +name = "butchunker_ffi" +version = "0.1.0" +dependencies = [ + "butchunker", + "libc", +] + [[package]] name = "butck_fixed_size" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 1918e4d..3c127c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,34 @@ name = "butchunker" version = "0.1.0" edition = "2024" +[features] +default = [] +ffi = [] + +[workspace] +members = [ + "ffi", +] + +[profile.dev] +opt-level = 0 +debug = true +split-debuginfo = "unpacked" +codegen-units = 16 +incremental = true +lto = false +panic = "unwind" + +[profile.release] +opt-level = 3 +debug = false +split-debuginfo = "off" +codegen-units = 1 +incremental = false +lto = "fat" +panic = "abort" +strip = "symbols" + [dependencies] butck_policies = { path = "policy/_policies" } diff --git a/build.rs b/build.rs index d69d05e..4c8cec0 100644 --- a/build.rs +++ b/build.rs @@ -1,13 +1,64 @@ +use std::{env, path::PathBuf, str::FromStr}; + fn main() { + build_butck_ffi(); + #[cfg(target_os = "windows")] build_win32_gui(); } +fn build_butck_ffi() { + // Check if cbindgen is installed + let cbindgen_check = std::process::Command::new("cbindgen") + .arg("--version") + .output(); + + if let Err(e) = cbindgen_check { + if e.kind() == std::io::ErrorKind::NotFound { + eprintln!("Error: cbindgen is not installed. Please install it with:"); + eprintln!(" cargo install cbindgen"); + std::process::exit(1); + } else { + eprintln!("Error: Failed to check cbindgen installation: {}", e); + std::process::exit(1); + } + } + + let target_dir = get_target_dir(); + + // Try to run cbindgen to generate C bindings + // > cbindgen --config cbindgen.toml ffi --output ffi/.temp/jvlib.h --quiet + let output = std::process::Command::new("cbindgen") + .args([ + "--config", + "cbindgen.toml", + ".", + "--output", + &format!("{}/lib_butck.h", target_dir.display()), + "--quiet", + ]) + .output(); + + match output { + Ok(output) if output.status.success() => { + // Successfully generated bindings + } + Ok(_) => { + eprintln!("cbindgen failed to generate bindings"); + } + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + eprintln!("cbindgen not found, skipping C binding generation"); + } + Err(e) => { + eprintln!("Failed to run cbindgen: {}", e); + } + } +} + fn build_win32_gui() { use std::fs; - use std::path::{Path, PathBuf}; + use std::path::Path; use std::process::Command; - use std::str::FromStr; const EXE_NAME: &str = "butckg.exe"; @@ -34,15 +85,7 @@ fn build_win32_gui() { .join("gui\\win32\\build\\bin") .join(EXE_NAME); if exe_path.exists() { - let out_dir = std::env::var("OUT_DIR").unwrap(); - let out_dir_path = PathBuf::from_str(&out_dir).unwrap(); - let target_dir = out_dir_path - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap(); + let target_dir = get_target_dir(); let dest_path = target_dir.join(EXE_NAME); if let Err(e) = fs::copy(&exe_path, &dest_path) { @@ -55,3 +98,16 @@ fn build_win32_gui() { eprintln!("Warning: Executable not found at: {:?}", exe_path); } } + +fn get_target_dir() -> PathBuf { + let out_dir = std::env::var("OUT_DIR").unwrap(); + let out_dir_path = PathBuf::from_str(&out_dir).unwrap(); + out_dir_path + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .to_path_buf() +} diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..65d71c6 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,45 @@ +language = "C" + +documentation = true +documentation_style = "doxy" + +line_length = 75 +tab_width = 2 +braces = "SameLine" + +header = """ +/*! + * This file is automatically generated by cbindgen. + * DO NOT EDIT THIS FILE MANUALLY. + * + * All string-returning functions allocate memory that must be freed using Butck_FreeString(). + */ +""" + +include_guard = "BUTCK_H" + +include_version = true + +[parse] +parse_deps = true +include = ["constants"] +extra_bindings = ["constants"] +expand = { crates = ["constants"] } + +[export] +include = ["*"] +exclude = [] + +[fn] +sort_by = "Name" +rename_args = "None" + +[const] +sort_by = "Name" + +[struct] +rename_fields = "None" + +[enum] +rename_variants = "None" +enum_class = false diff --git a/ffi/Cargo.lock b/ffi/Cargo.lock new file mode 100644 index 0000000..78bcaff --- /dev/null +++ b/ffi/Cargo.lock @@ -0,0 +1,823 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "butchunker" +version = "0.1.0" +dependencies = [ + "blake3", + "butck_policies", + "colored", + "env_logger", + "futures", + "hex", + "just_fmt", + "just_progress", + "just_template", + "log", + "memmap2", + "sha2", + "syn", + "thiserror", + "tokio", + "toml", +] + +[[package]] +name = "butchunker_ffi" +version = "0.1.0" +dependencies = [ + "butchunker", + "libc", +] + +[[package]] +name = "butck_fixed_size" +version = "0.1.0" + +[[package]] +name = "butck_policies" +version = "0.1.0" +dependencies = [ + "butck_fixed_size", + "thiserror", + "tokio", +] + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "just_fmt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e" + +[[package]] +name = "just_progress" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef1a564328a5061a4828b4f82b7275a7f3dbc7d4ed5778da986f6ab48563c88" +dependencies = [ + "tokio", +] + +[[package]] +name = "just_template" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3edb658c34b10b69c4b3b58f7ba989cd09c82c0621dee1eef51843c2327225" +dependencies = [ + "just_fmt", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml new file mode 100644 index 0000000..64f311e --- /dev/null +++ b/ffi/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "butchunker_ffi" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib", "staticlib"] +name = "lib_butck" + +[dependencies] +libc = "0.2" +butchunker = { path = "../" } diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs new file mode 100644 index 0000000..46503ed --- /dev/null +++ b/ffi/src/lib.rs @@ -0,0 +1,12 @@ +pub use butchunker::ctx::ffi::*; + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_FreeString(ptr: *mut libc::c_char) { + if ptr.is_null() { + return; + } + unsafe { + drop(std::ffi::CString::from_raw(ptr)); + } +} diff --git a/src/chunker/context.rs b/src/chunker/context.rs index 45283bc..e351b18 100644 --- a/src/chunker/context.rs +++ b/src/chunker/context.rs @@ -1,15 +1,16 @@ -use std::{collections::HashMap, env::current_dir, path::PathBuf, process::exit, str::FromStr}; - -use log::{error, warn}; - use crate::{ chunker::{constants::BUTCK_METADATA_DIR_NAME, rw::storage::hash::ChunkWriteHash}, special_argument, special_flag, storage::{ButckRWError, build, write}, utils::file_input_solve::parse_path_input, }; +use log::{error, warn}; +use std::{collections::HashMap, env::current_dir, path::PathBuf, process::exit, str::FromStr}; + +pub mod ffi; pub struct Butck; + impl Butck { pub fn write(files: Vec, storage: PathBuf) -> ButckContext { ButckContext::default() @@ -227,8 +228,8 @@ impl ButckContext { } } - fn apply_memmap_read(&mut self, args: &mut Vec) -> bool { - special_flag!(args, "-m", "--memmap-read") + fn apply_memmap_read(&mut self, args: &mut Vec) { + self.memmap_read = special_flag!(args, "-m", "--memmap-read"); } fn apply_register_name(&mut self, args: &mut Vec) { diff --git a/src/chunker/context/ffi.rs b/src/chunker/context/ffi.rs new file mode 100644 index 0000000..c169c18 --- /dev/null +++ b/src/chunker/context/ffi.rs @@ -0,0 +1,795 @@ +#![allow(unused_unsafe)] +use std::{collections::HashMap, env::current_dir, path::PathBuf}; + +use crate::chunker::{ + context::{ButckContext, ButckMethod}, + rw::storage::hash::ChunkWriteHash, +}; + +/// Helper macro to convert C string pointer to Option +/// Returns None if pointer is null or conversion fails +macro_rules! cstr_to_opt_string { + ($ptr:expr) => { + if $ptr.is_null() { + None + } else { + unsafe { + std::ffi::CStr::from_ptr($ptr) + .to_str() + .ok() + .map(|s| s.to_string()) + } + } + }; +} + +/// Helper macro to convert C string pointer to PathBuf +/// Returns empty PathBuf if pointer is null or conversion fails +macro_rules! cstr_to_path { + ($ptr:expr) => { + if $ptr.is_null() { + PathBuf::new() + } else { + unsafe { + std::ffi::CStr::from_ptr($ptr) + .to_str() + .map(PathBuf::from) + .unwrap_or_default() + } + } + }; +} + +/// Helper macro to convert C string pointer to PathBuf with fallback to current directory +/// Returns current directory if pointer is null or conversion fails +macro_rules! cstr_to_path_or_current_dir { + ($ptr:expr) => { + if $ptr.is_null() { + current_dir().unwrap() + } else { + unsafe { + std::ffi::CStr::from_ptr($ptr) + .to_str() + .map(PathBuf::from) + .unwrap_or_else(|_| current_dir().unwrap()) + } + } + }; +} + +#[repr(C)] +pub struct FFIButckContext { + method: u8, + file_paths: *mut *mut std::os::raw::c_char, + file_paths_len: usize, + storage_path: *mut std::os::raw::c_char, + display_boundaries: bool, + stream_read: u32, + stream_read_set: bool, + memmap_read: bool, + register_name: *mut std::os::raw::c_char, + policy_name: *mut std::os::raw::c_char, + chunk_hash: u8, + output_dir: *mut std::os::raw::c_char, + output_file: *mut std::os::raw::c_char, + params_keys: *mut *mut std::os::raw::c_char, + params_values: *mut *mut std::os::raw::c_char, + params_len: usize, +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_Write( + files: *mut *mut std::os::raw::c_char, + files_len: usize, + storage: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + let mut file_paths = Vec::new(); + if !files.is_null() && files_len > 0 { + for i in 0..files_len { + unsafe { + let ptr = *files.add(i); + if !ptr.is_null() { + let c_str = std::ffi::CStr::from_ptr(ptr); + if let Ok(path_str) = c_str.to_str() { + file_paths.push(PathBuf::from(path_str)); + } + } + } + } + } + + let storage_path = if !storage.is_null() { + unsafe { + let c_str = std::ffi::CStr::from_ptr(storage); + if let Ok(path_str) = c_str.to_str() { + PathBuf::from(path_str) + } else { + PathBuf::new() + } + } + } else { + PathBuf::new() + }; + + let ctx = ButckContext::default() + .with_file_paths(file_paths) + .with_storage_path(storage_path) + .with_write_mode(); + + Box::into_raw(Box::new(FFIButckContext::from(ctx))) +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_Build( + index_files: *mut *mut std::os::raw::c_char, + index_files_len: usize, + storage: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + let mut file_paths = Vec::new(); + if !index_files.is_null() && index_files_len > 0 { + for i in 0..index_files_len { + unsafe { + let ptr = *index_files.add(i); + if !ptr.is_null() { + let c_str = std::ffi::CStr::from_ptr(ptr); + if let Ok(path_str) = c_str.to_str() { + file_paths.push(PathBuf::from(path_str)); + } + } + } + } + } + + let storage_path = if !storage.is_null() { + unsafe { + let c_str = std::ffi::CStr::from_ptr(storage); + if let Ok(path_str) = c_str.to_str() { + PathBuf::from(path_str) + } else { + PathBuf::new() + } + } + } else { + PathBuf::new() + }; + + let ctx = ButckContext::default() + .with_file_paths(file_paths) + .with_storage_path(storage_path) + .with_build_mode(); + + Box::into_raw(Box::new(FFIButckContext::from(ctx))) +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_Exec(ctx: *mut FFIButckContext) -> bool { + if ctx.is_null() { + return false; + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let butck_ctx = ButckContext::from(*ffi_ctx); + // ffi_ctx is consumed by the dereference above, Box will be dropped automatically + + let runtime = tokio::runtime::Runtime::new().unwrap(); + match runtime.block_on(butck_ctx.exec()) { + Ok(_) => true, + Err(_) => false, + } + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithStoragePath( + ctx: *mut FFIButckContext, + storage: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let storage_path = cstr_to_path!(storage); + + butck_ctx = butck_ctx.with_storage_path(storage_path); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithDisplayBoundaries( + ctx: *mut FFIButckContext, + display: bool, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + butck_ctx = butck_ctx.with_display_boundaries(display); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithStreamRead( + ctx: *mut FFIButckContext, + size: u32, + set: bool, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let stream_read = if set { Some(size) } else { None }; + butck_ctx = butck_ctx.with_stream_read(stream_read); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithMemmapRead( + ctx: *mut FFIButckContext, + use_memmap: bool, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + butck_ctx = butck_ctx.with_memmap_read(use_memmap); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithRegisterName( + ctx: *mut FFIButckContext, + name: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let register_name = cstr_to_opt_string!(name); + + butck_ctx = butck_ctx.with_register_name(register_name); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithPolicyName( + ctx: *mut FFIButckContext, + name: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let policy_name = cstr_to_opt_string!(name); + + butck_ctx = butck_ctx.with_policy_name(policy_name); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithChunkHash(ctx: *mut FFIButckContext, hash: u8) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let chunk_hash = match hash { + 0 => ChunkWriteHash::Blake3, + 1 => ChunkWriteHash::Sha256, + _ => ChunkWriteHash::default(), + }; + + butck_ctx = butck_ctx.with_chunk_hash(chunk_hash); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithOutputDir( + ctx: *mut FFIButckContext, + dir: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let output_dir = cstr_to_path_or_current_dir!(dir); + + butck_ctx = butck_ctx.with_output_dir(output_dir); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithOutputFile( + ctx: *mut FFIButckContext, + file: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let output_file = cstr_to_opt_string!(file).map(PathBuf::from); + + butck_ctx = butck_ctx.with_output_file(output_file); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_WithFilePaths( + ctx: *mut FFIButckContext, + files: *mut *mut std::os::raw::c_char, + files_len: usize, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + let mut file_paths = Vec::new(); + if !files.is_null() && files_len > 0 { + for i in 0..files_len { + let ptr = *files.add(i); + if !ptr.is_null() { + let c_str = std::ffi::CStr::from_ptr(ptr); + if let Ok(path_str) = c_str.to_str() { + file_paths.push(PathBuf::from(path_str)); + } + } + } + } + + butck_ctx = butck_ctx.with_file_paths(file_paths); + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_AddFile( + ctx: *mut FFIButckContext, + file: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + if !file.is_null() { + let c_str = std::ffi::CStr::from_ptr(file); + if let Ok(path_str) = c_str.to_str() { + butck_ctx = butck_ctx.add_file(PathBuf::from(path_str)); + } + } + + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_Param( + ctx: *mut FFIButckContext, + key: *mut std::os::raw::c_char, + value: *mut std::os::raw::c_char, +) -> *mut FFIButckContext { + if ctx.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + let ffi_ctx = Box::from_raw(ctx); + let mut butck_ctx = ButckContext::from(*ffi_ctx); + + if !key.is_null() && !value.is_null() { + let key_cstr = std::ffi::CStr::from_ptr(key); + let value_cstr = std::ffi::CStr::from_ptr(value); + + if let (Ok(key_str), Ok(value_str)) = (key_cstr.to_str(), value_cstr.to_str()) { + butck_ctx = butck_ctx.param(key_str.to_string(), value_str.to_string()); + } + } + + Box::into_raw(Box::new(FFIButckContext::from(butck_ctx))) + } +} + +#[unsafe(no_mangle)] +#[allow(nonstandard_style)] +pub extern "C" fn Butck_FreeButckContext(ctx: *mut FFIButckContext) { + if ctx.is_null() { + return; + } + + unsafe { + let ffi_ctx = &mut *ctx; + + // Free file_paths + if !ffi_ctx.file_paths.is_null() && ffi_ctx.file_paths_len > 0 { + for i in 0..ffi_ctx.file_paths_len { + let ptr = *ffi_ctx.file_paths.add(i); + if !ptr.is_null() { + let _ = std::ffi::CString::from_raw(ptr); + } + } + let _ = Box::from_raw(std::slice::from_raw_parts_mut( + ffi_ctx.file_paths, + ffi_ctx.file_paths_len, + )); + } + + // Free storage_path + if !ffi_ctx.storage_path.is_null() { + let _ = std::ffi::CString::from_raw(ffi_ctx.storage_path); + } + + // Free register_name + if !ffi_ctx.register_name.is_null() { + let _ = std::ffi::CString::from_raw(ffi_ctx.register_name); + } + + // Free policy_name + if !ffi_ctx.policy_name.is_null() { + let _ = std::ffi::CString::from_raw(ffi_ctx.policy_name); + } + + // Free output_dir + if !ffi_ctx.output_dir.is_null() { + let _ = std::ffi::CString::from_raw(ffi_ctx.output_dir); + } + + // Free output_file + if !ffi_ctx.output_file.is_null() { + let _ = std::ffi::CString::from_raw(ffi_ctx.output_file); + } + + // Free params + if !ffi_ctx.params_keys.is_null() + && !ffi_ctx.params_values.is_null() + && ffi_ctx.params_len > 0 + { + for i in 0..ffi_ctx.params_len { + let key_ptr = *ffi_ctx.params_keys.add(i); + let value_ptr = *ffi_ctx.params_values.add(i); + + if !key_ptr.is_null() { + let _ = std::ffi::CString::from_raw(key_ptr); + } + if !value_ptr.is_null() { + let _ = std::ffi::CString::from_raw(value_ptr); + } + } + + let _ = Box::from_raw(std::slice::from_raw_parts_mut( + ffi_ctx.params_keys, + ffi_ctx.params_len, + )); + let _ = Box::from_raw(std::slice::from_raw_parts_mut( + ffi_ctx.params_values, + ffi_ctx.params_len, + )); + } + + // Free the context itself + let _ = Box::from_raw(ctx); + } +} + +impl From for FFIButckContext { + fn from(ctx: ButckContext) -> Self { + // Convert method enum + let method = match ctx.method { + ButckMethod::None => 0, + ButckMethod::Write => 1, + ButckMethod::Build => 2, + }; + + // Convert file paths + let mut file_paths_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); + for path in &ctx.file_paths { + if let Some(c_str) = path.to_str() { + let c_string = std::ffi::CString::new(c_str).unwrap(); + file_paths_vec.push(c_string.into_raw()); + } else { + file_paths_vec.push(std::ptr::null_mut()); + } + } + let file_paths_len = file_paths_vec.len(); + let file_paths = if file_paths_len > 0 { + let mut boxed_slice = file_paths_vec.into_boxed_slice(); + let ptr = boxed_slice.as_mut_ptr(); + std::mem::forget(boxed_slice); + ptr + } else { + std::ptr::null_mut() + }; + + // Convert storage path + let storage_path = if let Some(ref path) = ctx.storage_path { + if let Some(c_str) = path.to_str() { + let c_string = std::ffi::CString::new(c_str).unwrap(); + c_string.into_raw() + } else { + std::ptr::null_mut() + } + } else { + std::ptr::null_mut() + }; + + // Convert stream read + let (stream_read, stream_read_set) = match ctx.stream_read { + Some(size) => (size, true), + None => (0, false), + }; + + // Convert register name + let register_name = if let Some(ref name) = ctx.register_name { + let c_string = std::ffi::CString::new(name.as_str()).unwrap(); + c_string.into_raw() + } else { + std::ptr::null_mut() + }; + + // Convert policy name + let policy_name = if let Some(ref name) = ctx.policy_name { + let c_string = std::ffi::CString::new(name.as_str()).unwrap(); + c_string.into_raw() + } else { + std::ptr::null_mut() + }; + + // Convert chunk hash + let chunk_hash = match ctx.chunk_hash { + ChunkWriteHash::Blake3 => 0, + ChunkWriteHash::Sha256 => 1, + }; + + // Convert output dir + let output_dir = if let Some(c_str) = ctx.output_dir.to_str() { + let c_string = std::ffi::CString::new(c_str).unwrap(); + c_string.into_raw() + } else { + std::ptr::null_mut() + }; + + // Convert output file + let output_file = if let Some(ref path) = ctx.output_file { + if let Some(c_str) = path.to_str() { + let c_string = std::ffi::CString::new(c_str).unwrap(); + c_string.into_raw() + } else { + std::ptr::null_mut() + } + } else { + std::ptr::null_mut() + }; + + // Convert params + let mut params_keys_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); + let mut params_values_vec: Vec<*mut std::os::raw::c_char> = Vec::new(); + + for (key, value) in &ctx.params { + let key_cstring = std::ffi::CString::new(key.as_str()).unwrap(); + let value_cstring = std::ffi::CString::new(value.as_str()).unwrap(); + params_keys_vec.push(key_cstring.into_raw()); + params_values_vec.push(value_cstring.into_raw()); + } + + let params_len = params_keys_vec.len(); + let params_keys = if params_len > 0 { + let mut boxed_slice = params_keys_vec.into_boxed_slice(); + let ptr = boxed_slice.as_mut_ptr(); + std::mem::forget(boxed_slice); + ptr + } else { + std::ptr::null_mut() + }; + + let params_values = if params_len > 0 { + let mut boxed_slice = params_values_vec.into_boxed_slice(); + let ptr = boxed_slice.as_mut_ptr(); + std::mem::forget(boxed_slice); + ptr + } else { + std::ptr::null_mut() + }; + + FFIButckContext { + method, + file_paths, + file_paths_len, + storage_path, + display_boundaries: ctx.display_boundaries, + stream_read, + stream_read_set, + memmap_read: ctx.memmap_read, + register_name, + policy_name, + chunk_hash, + output_dir, + output_file, + params_keys, + params_values, + params_len, + } + } +} + +impl From for ButckContext { + fn from(ffi_ctx: FFIButckContext) -> Self { + // Convert method enum + let method = match ffi_ctx.method { + 0 => ButckMethod::None, + 1 => ButckMethod::Write, + 2 => ButckMethod::Build, + _ => ButckMethod::None, + }; + + // Convert file paths + let mut file_paths = Vec::new(); + if !ffi_ctx.file_paths.is_null() && ffi_ctx.file_paths_len > 0 { + for i in 0..ffi_ctx.file_paths_len { + unsafe { + let ptr = *ffi_ctx.file_paths.add(i); + if !ptr.is_null() { + let c_str = std::ffi::CStr::from_ptr(ptr); + if let Ok(path_str) = c_str.to_str() { + file_paths.push(PathBuf::from(path_str)); + } + } + } + } + } + + // Convert storage path + let storage_path = if !ffi_ctx.storage_path.is_null() { + unsafe { cstr_to_opt_string!(ffi_ctx.storage_path).map(PathBuf::from) } + } else { + None + }; + + // Convert stream read + let stream_read = if ffi_ctx.stream_read_set { + Some(ffi_ctx.stream_read) + } else { + None + }; + + // Convert register name + let register_name = if !ffi_ctx.register_name.is_null() { + unsafe { cstr_to_opt_string!(ffi_ctx.register_name) } + } else { + None + }; + + // Convert policy name + let policy_name = if !ffi_ctx.policy_name.is_null() { + unsafe { cstr_to_opt_string!(ffi_ctx.policy_name) } + } else { + None + }; + + // Convert chunk hash + let chunk_hash = match ffi_ctx.chunk_hash { + 0 => ChunkWriteHash::Blake3, + 1 => ChunkWriteHash::Sha256, + _ => ChunkWriteHash::default(), + }; + + // Convert output dir + let output_dir = if !ffi_ctx.output_dir.is_null() { + unsafe { cstr_to_path_or_current_dir!(ffi_ctx.output_dir) } + } else { + current_dir().unwrap() + }; + + // Convert output file + let output_file = if !ffi_ctx.output_file.is_null() { + unsafe { cstr_to_opt_string!(ffi_ctx.output_file).map(PathBuf::from) } + } else { + None + }; + + // Convert params + let mut params = HashMap::new(); + if !ffi_ctx.params_keys.is_null() + && !ffi_ctx.params_values.is_null() + && ffi_ctx.params_len > 0 + { + for i in 0..ffi_ctx.params_len { + unsafe { + let key_ptr = *ffi_ctx.params_keys.add(i); + let value_ptr = *ffi_ctx.params_values.add(i); + + if !key_ptr.is_null() && !value_ptr.is_null() { + let key_cstr = std::ffi::CStr::from_ptr(key_ptr); + let value_cstr = std::ffi::CStr::from_ptr(value_ptr); + + if let (Ok(key_str), Ok(value_str)) = + (key_cstr.to_str(), value_cstr.to_str()) + { + params.insert(key_str.to_string(), value_str.to_string()); + } + } + } + } + } + + ButckContext { + method, + file_paths, + storage_path, + display_boundaries: ffi_ctx.display_boundaries, + stream_read, + memmap_read: ffi_ctx.memmap_read, + register_name, + policy_name, + chunk_hash, + output_dir, + output_file, + params, + ..Default::default() + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5c2c190..9ae83cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,9 @@ pub mod storage { pub use crate::chunker::rw::storage::build::*; pub use crate::chunker::rw::storage::write::*; } + +pub mod ctx { + #[cfg(feature = "ffi")] + pub use crate::chunker::context::ffi::*; + pub use crate::chunker::context::*; +} -- cgit