From 320958d888fcd05b36d9f0fa8b9b29aeddb84582 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 13:56:51 -0800 Subject: [PATCH] ctypes approach --- Cargo.toml | 4 +- klamath_rs_ext/__init__.py | 2 +- klamath_rs_ext/basic.py | 71 +++++++++++++++++++++------ pyproject.toml | 32 +++++++++++-- src/lib.rs | 98 +++++++++++--------------------------- 5 files changed, 115 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 41cbb68..a211306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "klamath_rs_ext" -version = "0.1.0" +version = "0.2.0" authors = ["jan "] edition = "2021" @@ -12,5 +12,3 @@ crate-type = ["cdylib", "rlib"] [dependencies] byteorder = "^1" -pyo3 = "^0" -numpy = "^0" diff --git a/klamath_rs_ext/__init__.py b/klamath_rs_ext/__init__.py index fa02fb7..d042d92 100644 --- a/klamath_rs_ext/__init__.py +++ b/klamath_rs_ext/__init__.py @@ -2,5 +2,5 @@ from .basic import pack_int2 as pack_int2 from .basic import pack_int4 as pack_int4 -__version__ = 0.1 +__version__ = 0.2 diff --git a/klamath_rs_ext/basic.py b/klamath_rs_ext/basic.py index 7237b1c..838b42f 100644 --- a/klamath_rs_ext/basic.py +++ b/klamath_rs_ext/basic.py @@ -1,24 +1,68 @@ from collections.abc import Sequence +import ctypes +from pathlib import Path +from itertools import chain import numpy from numpy.typing import NDArray -from .klamath_rs_ext import arr_to_int2, arr_to_int4 +so_path = Path(__file__).resolve().parent / 'libklamath_rs_ext.so' +clib = ctypes.CDLL(so_path) + + +CONV_TABLE_i16 = { + numpy.float64: clib.f64_to_i16, + numpy.float32: clib.f32_to_i16, + numpy.int64: clib.i64_to_i16, + numpy.int32: clib.i32_to_i16, + numpy.int16: clib.i16_to_i16, + numpy.uint64: clib.u64_to_i16, + numpy.uint32: clib.u32_to_i16, + numpy.uint16: clib.u16_to_i16, + } + +CONV_TABLE_i32 = { + numpy.float64: clib.f64_to_i32, + numpy.float32: clib.f32_to_i32, + numpy.int64: clib.i64_to_i32, + numpy.int32: clib.i32_to_i32, + numpy.uint64: clib.u64_to_i32, + numpy.uint32: clib.u32_to_i32, + } + +clib.f64_to_i16.restype = ctypes.c_double +clib.f32_to_i16.restype = ctypes.c_float +clib.i64_to_i16.restype = ctypes.c_int64 +clib.i32_to_i16.restype = ctypes.c_int32 +clib.i16_to_i16.restype = ctypes.c_int16 +clib.u64_to_i16.restype = ctypes.c_uint64 +clib.u32_to_i16.restype = ctypes.c_uint32 +clib.u16_to_i16.restype = ctypes.c_uint16 + +clib.f64_to_i32.restype = ctypes.c_double +clib.f32_to_i32.restype = ctypes.c_float +clib.i64_to_i32.restype = ctypes.c_int64 +clib.i32_to_i32.restype = ctypes.c_int32 +clib.u64_to_i32.restype = ctypes.c_uint64 +clib.u32_to_i32.restype = ctypes.c_uint32 + + +for fn in chain(CONV_TABLE_i16.values(), CONV_TABLE_i32.values()): + fn.argtypes = [ctypes.POINTER(fn.restype), ctypes.c_size_t] + def pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes: arr = numpy.asarray(data) - if arr.dtype in ( - numpy.float64, numpy.float32, - numpy.int64, numpy.uint64, - numpy.int32, numpy.uint32, - numpy.int16, numpy.uint16, - ): + if arr.dtype in CONV_TABLE_i16.keys(): arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA')) if arr is data: arr = numpy.array(arr, copy=True) - arr_to_int2(arr) + + fn = CONV_TABLE_i16[arr.dtype] + result = fn(arr.ctypes.data_as(fn.argtypes[0]), arr.size) + i2arr = arr.view('>i2')[::arr.itemsize // 2] return i2arr.tobytes() @@ -34,15 +78,14 @@ def pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes: def pack_int4(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes: arr = numpy.asarray(data) - if arr.dtype in ( - numpy.float64, numpy.float32, - numpy.int64, numpy.uint64, - numpy.int32, numpy.uint32, - ): + if arr.dtype in CONV_TABLE_i32.keys(): arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA')) if arr is data: arr = numpy.array(arr, copy=True) - arr_to_int4(arr) + + fn = CONV_TABLE_i32[arr.dtype] + result = fn(arr.ctypes.data_as(fn.argtypes[0]), arr.size) + i4arr = arr.view('>i4')[::arr.itemsize // 4] return i4arr.tobytes() diff --git a/pyproject.toml b/pyproject.toml index 9704ea5..97d888c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,32 @@ requires = ["maturin>1.0,<2.0"] build-backend = "maturin" - -[tool.maturin] -features = ["pyo3/extension-module"] +[project] +name = "klamath_rs_ext" +description = "Compiled extensions for klamath GDS library" +#readme = "README.md" +#license = { file = "LICENSE.md" } +authors = [ + { name="Jan Petykiewicz", email="jan@mpxd.net" }, + ] +homepage = "https://mpxd.net/code/jan/klamath-rs" +repository = "https://mpxd.net/code/jan/klamath-rs" +classifiers = [ + "Programming Language :: Python :: 3", + "Development Status :: 4 - Beta", +# "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Manufacturing", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + ] +requires-python = ">=3.11" +#include = [ +# "LICENSE.md" +# ] +dynamic = ["version"] +dependencies = [ + "cffi", + ] diff --git a/src/lib.rs b/src/lib.rs index b64ab5d..84e8551 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,87 +7,43 @@ pub mod elements; pub mod library; -//use ndarray; -use numpy::{PyArray1, PyUntypedArray, PyUntypedArrayMethods, PyArrayDescrMethods, PyArrayMethods, dtype}; -use pyo3::prelude::{Python, pymodule, PyModule, PyResult, Bound, wrap_pyfunction, pyfunction, PyModuleMethods, PyAnyMethods}; -use pyo3::exceptions::{PyValueError, PyTypeError}; +use rust_util::ToInt2BE; +use rust_util::ToInt4BE; -#[pymodule] -fn klamath_rs_ext(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(arr_to_int2, m)?)?; - m.add_function(wrap_pyfunction!(arr_to_int4, m)?)?; - Ok(()) -} - - -#[pyfunction] -fn arr_to_int2(py: Python<'_>, pyarr: &Bound<'_, PyUntypedArray>) -> PyResult<()> { - use rust_util::ToInt2BE; - - assert!(pyarr.is_c_contiguous(), "Array must be c-contiguous!"); - - macro_rules! i2if { - ( $el_type:expr, $tt:ty ) => { - if $el_type.is_equiv_to(&dtype::<$tt>(py)) { - let arr = pyarr.downcast::>()?; - let mut array = unsafe { arr.as_array_mut() }; - for xx in array.iter_mut() { - *xx = <$tt>::convert_to_i2be(*xx).map_err( - |e| PyValueError::new_err(format!("Invalid value for 2-byte int: {}", e)) - )?; +macro_rules! mkfun { + ( $fname:ident, $tt:ty, $elfn:ident ) => { + #[no_mangle] + pub extern "C" fn $fname(arr: *mut $tt, size: usize) -> $tt { + let sl = unsafe { std::slice::from_raw_parts_mut(arr, size) }; + for xx in sl.iter_mut() { + let res = <$tt>::$elfn(*xx); + match res { + Err(cc) => return cc, + Ok(cc) => { *xx = cc; }, } - return Ok(()) } + 0 as $tt } } - - - let el_type = pyarr.dtype(); - i2if!(el_type, f64); - i2if!(el_type, f32); - i2if!(el_type, i64); - i2if!(el_type, u64); - i2if!(el_type, i32); - i2if!(el_type, u32); - i2if!(el_type, i16); - i2if!(el_type, u16); - - Err(PyTypeError::new_err(format!("arr_to_int2 not implemented for type {:?}", el_type))) } -#[pyfunction] -fn arr_to_int4(py: Python<'_>, pyarr: &Bound<'_, PyUntypedArray>) -> PyResult<()> { - use rust_util::ToInt4BE; +mkfun!(f64_to_i16, f64, convert_to_i2be); +mkfun!(f32_to_i16, f32, convert_to_i2be); +mkfun!(i64_to_i16, i64, convert_to_i2be); +mkfun!(u64_to_i16, u64, convert_to_i2be); +mkfun!(i32_to_i16, i32, convert_to_i2be); +mkfun!(u32_to_i16, u32, convert_to_i2be); +mkfun!(i16_to_i16, i16, convert_to_i2be); +mkfun!(u16_to_i16, u16, convert_to_i2be); - assert!(pyarr.is_c_contiguous(), "Array must be c-contiguous!"); - - macro_rules! i4if { - ( $el_type:expr, $tt:ty ) => { - if $el_type.is_equiv_to(&dtype::<$tt>(py)) { - let arr = pyarr.downcast::>()?; - let mut array = unsafe { arr.as_array_mut() }; - for xx in array.iter_mut() { - *xx = <$tt>::convert_to_i4be(*xx).map_err( - |e| PyValueError::new_err(format!("Invalid value for 4-byte int: {}", e)) - )?; - } - return Ok(()) - } - } - } - - let el_type = pyarr.dtype(); - i4if!(el_type, f64); - i4if!(el_type, f32); - i4if!(el_type, i64); - i4if!(el_type, u64); - i4if!(el_type, i32); - i4if!(el_type, u32); - - Err(PyTypeError::new_err(format!("arr_to_int4 not implemented for type {:?}", el_type))) -} +mkfun!(f64_to_i32, f64, convert_to_i4be); +mkfun!(f32_to_i32, f32, convert_to_i4be); +mkfun!(i64_to_i32, i64, convert_to_i4be); +mkfun!(u64_to_i32, u64, convert_to_i4be); +mkfun!(i32_to_i32, i32, convert_to_i4be); +mkfun!(u32_to_i32, u32, convert_to_i4be); mod rust_util {