diff --git a/crates/capi/src/lib.rs b/crates/capi/src/lib.rs index 70c2226e67f..35158e394de 100644 --- a/crates/capi/src/lib.rs +++ b/crates/capi/src/lib.rs @@ -17,6 +17,7 @@ pub mod pyerrors; pub mod pylifecycle; pub mod pystate; pub mod refcount; +pub mod tupleobject; pub mod unicodeobject; mod util; diff --git a/crates/capi/src/tupleobject.rs b/crates/capi/src/tupleobject.rs new file mode 100644 index 00000000000..985141f6d4c --- /dev/null +++ b/crates/capi/src/tupleobject.rs @@ -0,0 +1,122 @@ +use crate::PyObject; +use crate::object::define_py_check; +use crate::pystate::with_vm; +use core::ffi::c_int; +use core::slice; +use rustpython_vm::PyResult; +use rustpython_vm::builtins::PyTuple; +use rustpython_vm::sliceable::SliceableSequenceOp; + +define_py_check!(fn PyTuple_Check, types.tuple_type); +define_py_check!(exact fn PyTuple_CheckExact, types.tuple_type); + +#[unsafe(no_mangle)] +pub extern "C" fn PyTuple_New(len: isize) -> *mut PyObject { + with_vm(|vm| { + if len == 0 { + return Ok(vm.ctx.empty_tuple.to_owned()); + } + + Err(vm.new_not_implemented_error( + "PyTuple_New for non zero sized tuples is not supported, use PyTuple_FromArray instead", + )) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyTuple_FromArray( + array: *const *mut PyObject, + size: isize, +) -> *mut PyObject { + with_vm(|vm| { + let size = size + .try_into() + .map_err(|_| vm.new_system_error("negative size passed to Tuple_FromArray"))?; + let slice = unsafe { slice::from_raw_parts(array, size) }; + let elements = slice + .iter() + .map(|ptr| unsafe { &**ptr }.to_owned()) + .collect::>(); + Ok(vm.new_tuple(elements)) + }) +} + +#[unsafe(no_mangle)] +pub extern "C" fn PyTuple_SetItem( + _tuple: *mut PyObject, + _pos: isize, + _value: *mut PyObject, +) -> c_int { + with_vm::, _>( + |vm| Err(vm.new_not_implemented_error("Tuple objects are immutable")), + ) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyTuple_Size(tuple: *mut PyObject) -> isize { + with_vm(|vm| { + let tuple = unsafe { &*tuple }.try_downcast_ref::(vm)?; + Ok(tuple.__len__()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyTuple_GetItem(tuple: *mut PyObject, pos: isize) -> *mut PyObject { + with_vm(|vm| { + let tuple = unsafe { &*tuple }.try_downcast_ref::(vm)?; + let result: &PyObject = pos + .try_into() + .ok() + .and_then(|index: usize| tuple.get(index)) + .ok_or_else(|| vm.new_index_error("tuple index out of range"))?; + + Ok(result.as_raw()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyTuple_GetSlice( + tuple: *mut PyObject, + low: isize, + high: isize, +) -> *mut PyObject { + with_vm(|vm| { + let tuple = unsafe { &*tuple }.try_downcast_ref::(vm)?; + let len = tuple.__len__() as isize; + let low = low.clamp(0, len); + let high = high.clamp(low, len); + let slice = tuple.do_slice(low as usize..high as usize); + Ok(vm.ctx.new_tuple(slice)) + }) +} + +#[cfg(false)] +mod tests { + use pyo3::prelude::*; + use pyo3::types::PyTuple; + + #[test] + fn test_empty_tuple() { + Python::attach(|py| { + let tuple = PyTuple::empty(py); + assert_eq!(tuple.len(), 0); + }) + } + + #[test] + fn test_tuple_into_python() { + Python::attach(|py| { + let tuple = (1, 2, 3).into_pyobject(py).unwrap(); + assert_eq!(tuple.len(), 3); + }) + } + + #[test] + fn test_tuple_get_slice() { + Python::attach(|py| { + let tuple = (1, 2, 3).into_pyobject(py).unwrap(); + let slice = tuple.get_slice(1, 2); + assert_eq!(slice.extract::<(u32,)>().unwrap(), (2,)); + }) + } +}