summaryrefslogtreecommitdiff
path: root/src/chunker/context/ffi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/chunker/context/ffi.rs')
-rw-r--r--src/chunker/context/ffi.rs795
1 files changed, 795 insertions, 0 deletions
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<String>
+/// 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<ButckContext> 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<FFIButckContext> 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()
+ }
+ }
+}