ctypes approach
This commit is contained in:
parent
ba07d253d2
commit
320958d888
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "klamath_rs_ext"
|
name = "klamath_rs_ext"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["jan <jan@mpxd.net>"]
|
authors = ["jan <jan@mpxd.net>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@ -12,5 +12,3 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "^1"
|
byteorder = "^1"
|
||||||
pyo3 = "^0"
|
|
||||||
numpy = "^0"
|
|
||||||
|
@ -2,5 +2,5 @@ from .basic import pack_int2 as pack_int2
|
|||||||
from .basic import pack_int4 as pack_int4
|
from .basic import pack_int4 as pack_int4
|
||||||
|
|
||||||
|
|
||||||
__version__ = 0.1
|
__version__ = 0.2
|
||||||
|
|
||||||
|
@ -1,24 +1,68 @@
|
|||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
import ctypes
|
||||||
|
from pathlib import Path
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
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:
|
def pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
|
||||||
arr = numpy.asarray(data)
|
arr = numpy.asarray(data)
|
||||||
|
|
||||||
if arr.dtype in (
|
if arr.dtype in CONV_TABLE_i16.keys():
|
||||||
numpy.float64, numpy.float32,
|
|
||||||
numpy.int64, numpy.uint64,
|
|
||||||
numpy.int32, numpy.uint32,
|
|
||||||
numpy.int16, numpy.uint16,
|
|
||||||
):
|
|
||||||
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
||||||
if arr is data:
|
if arr is data:
|
||||||
arr = numpy.array(arr, copy=True)
|
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]
|
i2arr = arr.view('>i2')[::arr.itemsize // 2]
|
||||||
return i2arr.tobytes()
|
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:
|
def pack_int4(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
|
||||||
arr = numpy.asarray(data)
|
arr = numpy.asarray(data)
|
||||||
|
|
||||||
if arr.dtype in (
|
if arr.dtype in CONV_TABLE_i32.keys():
|
||||||
numpy.float64, numpy.float32,
|
|
||||||
numpy.int64, numpy.uint64,
|
|
||||||
numpy.int32, numpy.uint32,
|
|
||||||
):
|
|
||||||
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
||||||
if arr is data:
|
if arr is data:
|
||||||
arr = numpy.array(arr, copy=True)
|
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]
|
i4arr = arr.view('>i4')[::arr.itemsize // 4]
|
||||||
return i4arr.tobytes()
|
return i4arr.tobytes()
|
||||||
|
|
||||||
|
@ -2,6 +2,32 @@
|
|||||||
requires = ["maturin>1.0,<2.0"]
|
requires = ["maturin>1.0,<2.0"]
|
||||||
build-backend = "maturin"
|
build-backend = "maturin"
|
||||||
|
|
||||||
|
[project]
|
||||||
[tool.maturin]
|
name = "klamath_rs_ext"
|
||||||
features = ["pyo3/extension-module"]
|
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",
|
||||||
|
]
|
||||||
|
106
src/lib.rs
106
src/lib.rs
@ -7,87 +7,43 @@ pub mod elements;
|
|||||||
pub mod library;
|
pub mod library;
|
||||||
|
|
||||||
|
|
||||||
//use ndarray;
|
use rust_util::ToInt2BE;
|
||||||
use numpy::{PyArray1, PyUntypedArray, PyUntypedArrayMethods, PyArrayDescrMethods, PyArrayMethods, dtype};
|
use rust_util::ToInt4BE;
|
||||||
use pyo3::prelude::{Python, pymodule, PyModule, PyResult, Bound, wrap_pyfunction, pyfunction, PyModuleMethods, PyAnyMethods};
|
|
||||||
use pyo3::exceptions::{PyValueError, PyTypeError};
|
|
||||||
|
|
||||||
|
|
||||||
#[pymodule]
|
macro_rules! mkfun {
|
||||||
fn klamath_rs_ext(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
( $fname:ident, $tt:ty, $elfn:ident ) => {
|
||||||
m.add_function(wrap_pyfunction!(arr_to_int2, m)?)?;
|
#[no_mangle]
|
||||||
m.add_function(wrap_pyfunction!(arr_to_int4, m)?)?;
|
pub extern "C" fn $fname(arr: *mut $tt, size: usize) -> $tt {
|
||||||
Ok(())
|
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; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0 as $tt
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[pyfunction]
|
mkfun!(f64_to_i16, f64, convert_to_i2be);
|
||||||
fn arr_to_int2(py: Python<'_>, pyarr: &Bound<'_, PyUntypedArray>) -> PyResult<()> {
|
mkfun!(f32_to_i16, f32, convert_to_i2be);
|
||||||
use rust_util::ToInt2BE;
|
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!");
|
mkfun!(f64_to_i32, f64, convert_to_i4be);
|
||||||
|
mkfun!(f32_to_i32, f32, convert_to_i4be);
|
||||||
macro_rules! i2if {
|
mkfun!(i64_to_i32, i64, convert_to_i4be);
|
||||||
( $el_type:expr, $tt:ty ) => {
|
mkfun!(u64_to_i32, u64, convert_to_i4be);
|
||||||
if $el_type.is_equiv_to(&dtype::<$tt>(py)) {
|
mkfun!(i32_to_i32, i32, convert_to_i4be);
|
||||||
let arr = pyarr.downcast::<PyArray1<$tt>>()?;
|
mkfun!(u32_to_i32, u32, convert_to_i4be);
|
||||||
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))
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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::<PyArray1<$tt>>()?;
|
|
||||||
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)))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mod rust_util {
|
mod rust_util {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user