From 4f61894e555ac3c1deaff393b1345bced95cfd33 Mon Sep 17 00:00:00 2001 From: Beinsezii Date: Fri, 5 Jul 2024 16:12:03 -0700 Subject: [PATCH] Add FFI for `str2space`, rename convert_space FFIs Rename `convert_space_ffi_3f32` -> `convert_space_3f32` along with other monotypes. This is because the functions aren't publicly exposed in Rust, so the `_ffi` qualifier is just excess Add `str2space_ffi` generic function and appropriate monotypes `impl TryFrom<*const c_char> for Space` to share code between FFIs --- scripts/test_ctypes.py | 14 +++++-- src/lib.rs | 95 ++++++++++++++++++++++++++---------------- 2 files changed, 70 insertions(+), 39 deletions(-) diff --git a/scripts/test_ctypes.py b/scripts/test_ctypes.py index db8c446..d431c18 100755 --- a/scripts/test_ctypes.py +++ b/scripts/test_ctypes.py @@ -14,13 +14,16 @@ colcon = ctypes.CDLL(f"./target/release/{LIBRARY}") -colcon.convert_space_ffi_3f32.argtypes = [ +colcon.convert_space_3f32.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, cpixels, ctypes.c_uint, ] -colcon.convert_space_ffi_3f32.restype = ctypes.c_int32 +colcon.convert_space_3f32.restype = ctypes.c_int32 + +colcon.str2space_3f32.argtypes = [ctypes.c_char_p, ctypes.c_char_p] +colcon.str2space_3f32.restype = cpixels # No way to have a known size? # up colcon.srgb_to_hsv_3f32.argtypes = [cpixel] @@ -141,6 +144,11 @@ def pixcmp(a, b): pixcmp(list(pix), HSV) pix = (ctypes.c_float * len(SRGB))(*SRGB) -if colcon.convert_space_ffi_3f32("srgb".encode(), "lch".encode(), pix, len(pix)) != 0: +if colcon.convert_space_3f32("srgb".encode(), "lch".encode(), pix, len(pix)) != 0: print("CONVERT SPACE FAIL") pixcmp(list(pix), LCH) + +pix = colcon.str2space_3f32(f"oklab {OKLAB}".encode(), "srgb".encode()) +pixcmp(pix[0:3], SRGB) +# validate null is utilized +assert not bool(colcon.str2space_3f32("cheese sandwhich".encode(), "srgb".encode())) diff --git a/src/lib.rs b/src/lib.rs index e824196..fca7539 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,7 @@ pub trait DType: + Rem + Sub + PartialOrd + + Debug + Display + FromF32 { @@ -558,6 +559,22 @@ impl TryFrom<&str> for Space { } } +impl TryFrom<*const c_char> for Space { + type Error = (); + fn try_from(value: *const c_char) -> Result { + if value.is_null() { + Err(()) + } else { + unsafe { CStr::from_ptr(value) } + .to_str() + .ok() + .map(|s| Self::try_from(s).ok()) + .flatten() + .ok_or(()) + } + } +} + impl Display for Space { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::fmt::write( @@ -755,38 +772,8 @@ pub fn convert_space_ffi( where Channels: ValidChannels, { - let from = unsafe { - if from.is_null() { - return 1; - } else { - if let Some(s) = CStr::from_ptr(from) - .to_str() - .ok() - .map(|s| Space::try_from(s).ok()) - .flatten() - { - s - } else { - return 1; - } - } - }; - let to = unsafe { - if to.is_null() { - return 2; - } else { - if let Some(s) = CStr::from_ptr(to) - .to_str() - .ok() - .map(|s| Space::try_from(s).ok()) - .flatten() - { - s - } else { - return 2; - } - } - }; + let Ok(from) = Space::try_from(from) else { return 1 }; + let Ok(to) = Space::try_from(to) else { return 2 }; let pixels = unsafe { if pixels.is_null() { return 3; @@ -905,6 +892,25 @@ where col }) } + +/// Same as `str2space` but with FFI types +/// +/// Returns an N-length pointer to T on success or null on failure +pub fn str2space_ffi(s: *const c_char, to: *const c_char) -> *const T +where + Channels: ValidChannels, +{ + if s.is_null() { + return core::ptr::null(); + }; + let Some(s) = unsafe { CStr::from_ptr(s) }.to_str().ok() else { + return core::ptr::null(); + }; + let Ok(to) = Space::try_from(to) else { + return core::ptr::null(); + }; + str2space::(s, to).map_or(core::ptr::null(), |b| Box::into_raw(Box::new(b)).cast()) +} // ### Str2Col ### }}} // ### FORWARD ### {{{ @@ -1316,22 +1322,39 @@ where // ### MONOTYPED EXTERNAL FUNCTIONS ### {{{ #[no_mangle] -extern "C" fn convert_space_ffi_3f32(from: *const c_char, to: *const c_char, pixels: *mut f32, len: usize) -> i32 { +extern "C" fn convert_space_3f32(from: *const c_char, to: *const c_char, pixels: *mut f32, len: usize) -> i32 { convert_space_ffi::<_, 3>(from, to, pixels, len) } #[no_mangle] -extern "C" fn convert_space_ffi_4f32(from: *const c_char, to: *const c_char, pixels: *mut f32, len: usize) -> i32 { +extern "C" fn convert_space_4f32(from: *const c_char, to: *const c_char, pixels: *mut f32, len: usize) -> i32 { convert_space_ffi::<_, 4>(from, to, pixels, len) } #[no_mangle] -extern "C" fn convert_space_ffi_3f64(from: *const c_char, to: *const c_char, pixels: *mut f64, len: usize) -> i32 { +extern "C" fn convert_space_3f64(from: *const c_char, to: *const c_char, pixels: *mut f64, len: usize) -> i32 { convert_space_ffi::<_, 3>(from, to, pixels, len) } #[no_mangle] -extern "C" fn convert_space_ffi_4f64(from: *const c_char, to: *const c_char, pixels: *mut f64, len: usize) -> i32 { +extern "C" fn convert_space_4f64(from: *const c_char, to: *const c_char, pixels: *mut f64, len: usize) -> i32 { convert_space_ffi::<_, 4>(from, to, pixels, len) } +#[no_mangle] +extern "C" fn str2space_3f32(s: *const c_char, to: *const c_char) -> *const f32 { + str2space_ffi::(s, to) +} +#[no_mangle] +extern "C" fn str2space_4f32(s: *const c_char, to: *const c_char) -> *const f32 { + str2space_ffi::(s, to) +} +#[no_mangle] +extern "C" fn str2space_3f64(s: *const c_char, to: *const c_char) -> *const f64 { + str2space_ffi::(s, to) +} +#[no_mangle] +extern "C" fn str2space_4f64(s: *const c_char, to: *const c_char) -> *const f64 { + str2space_ffi::(s, to) +} + macro_rules! cdef1 { ($base:ident, $f32:ident, $f64:ident) => { #[no_mangle]