Skip to content

Commit

Permalink
Add FFI for str2space, rename convert_space FFIs
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Beinsezii committed Jul 5, 2024
1 parent d1abda4 commit 4f61894
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 39 deletions.
14 changes: 11 additions & 3 deletions scripts/test_ctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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()))
95 changes: 59 additions & 36 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub trait DType:
+ Rem<Output = Self>
+ Sub<Output = Self>
+ PartialOrd
+ Debug
+ Display
+ FromF32
{
Expand Down Expand Up @@ -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<Self, ()> {
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(
Expand Down Expand Up @@ -755,38 +772,8 @@ pub fn convert_space_ffi<T: DType, const N: usize>(
where
Channels<N>: 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;
Expand Down Expand Up @@ -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<T: DType, const N: usize>(s: *const c_char, to: *const c_char) -> *const T
where
Channels<N>: 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::<T, N>(s, to).map_or(core::ptr::null(), |b| Box::into_raw(Box::new(b)).cast())
}
// ### Str2Col ### }}}

// ### FORWARD ### {{{
Expand Down Expand Up @@ -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::<f32, 3>(s, to)
}
#[no_mangle]
extern "C" fn str2space_4f32(s: *const c_char, to: *const c_char) -> *const f32 {
str2space_ffi::<f32, 4>(s, to)
}
#[no_mangle]
extern "C" fn str2space_3f64(s: *const c_char, to: *const c_char) -> *const f64 {
str2space_ffi::<f64, 3>(s, to)
}
#[no_mangle]
extern "C" fn str2space_4f64(s: *const c_char, to: *const c_char) -> *const f64 {
str2space_ffi::<f64, 4>(s, to)
}

macro_rules! cdef1 {
($base:ident, $f32:ident, $f64:ident) => {
#[no_mangle]
Expand Down

0 comments on commit 4f61894

Please sign in to comment.