diff --git a/crates/capi/src/lib.rs b/crates/capi/src/lib.rs index 2daee1f260f..15230958a25 100644 --- a/crates/capi/src/lib.rs +++ b/crates/capi/src/lib.rs @@ -26,6 +26,7 @@ pub mod pylifecycle; pub mod pystate; pub mod refcount; pub mod setobject; +pub mod sliceobject; pub mod traceback; pub mod tupleobject; pub mod unicodeobject; diff --git a/crates/capi/src/sliceobject.rs b/crates/capi/src/sliceobject.rs new file mode 100644 index 00000000000..2a625fab523 --- /dev/null +++ b/crates/capi/src/sliceobject.rs @@ -0,0 +1,106 @@ +use crate::PyObject; +use crate::pystate::with_vm; +use core::ffi::c_int; +use rustpython_vm::PyPayload; +use rustpython_vm::builtins::PySlice; +use rustpython_vm::sliceable::SaturatedSlice; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PySlice_New( + start: *mut PyObject, + stop: *mut PyObject, + step: *mut PyObject, +) -> *mut PyObject { + with_vm(|vm| { + let start = if start.is_null() { + None + } else { + Some(unsafe { &*start }.to_owned()) + }; + let stop = if stop.is_null() { + vm.ctx.none() + } else { + unsafe { &*stop }.to_owned() + }; + let step = if step.is_null() { + None + } else { + Some(unsafe { &*step }.to_owned()) + }; + Ok(PySlice { start, stop, step }.into_ref(&vm.ctx)) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PySlice_Unpack( + slice: *mut PyObject, + start: *mut isize, + stop: *mut isize, + step: *mut isize, +) -> c_int { + with_vm(|vm| { + let slice = unsafe { &*slice }.try_downcast_ref::(vm)?; + let saturated = slice.to_saturated(vm)?; + unsafe { + *start = saturated.start(); + *stop = saturated.stop(); + *step = saturated.step(); + } + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PySlice_AdjustIndices( + length: isize, + start: *mut isize, + stop: *mut isize, + step: isize, +) -> isize { + let length = length.max(0) as usize; + let saturated = SaturatedSlice::from_parts(unsafe { *start }, unsafe { *stop }, step); + let (range, _, slice_len) = saturated.adjust_indices(length); + unsafe { + if step.is_negative() { + *start = range.end as isize - 1; + *stop = range.start as isize - 1; + } else { + *start = range.start as isize; + *stop = range.end as isize; + } + } + slice_len as isize +} + +#[cfg(false)] +mod tests { + use pyo3::prelude::*; + use pyo3::types::{PySlice, PySliceMethods}; + + #[test] + fn slice_new_indices() { + Python::attach(|py| { + let slice = PySlice::new(py, 1, 10, 3); + let indices = slice.indices(100).unwrap(); + assert_eq!((indices.start, indices.stop, indices.step), (1, 10, 3)); + }) + } + + #[test] + fn slice_full_defaults() { + Python::attach(|py| { + let slice = PySlice::full(py); + let indices = slice.indices(5).unwrap(); + assert_eq!((indices.start, indices.stop, indices.step), (0, 5, 1)); + }) + } + + #[test] + fn slice_new_negative_step() { + Python::attach(|py| { + let slice = PySlice::new(py, 10, 1, -2); + let indices = slice.indices(100).unwrap(); + assert_eq!((indices.start, indices.stop, indices.step), (10, 1, -2)); + }) + } +} diff --git a/crates/vm/src/sliceable.rs b/crates/vm/src/sliceable.rs index dd4dd50979d..b0f4c7808ff 100644 --- a/crates/vm/src/sliceable.rs +++ b/crates/vm/src/sliceable.rs @@ -347,6 +347,26 @@ pub struct SaturatedSlice { } impl SaturatedSlice { + #[must_use] + pub const fn from_parts(start: isize, stop: isize, step: isize) -> Self { + Self { start, stop, step } + } + + #[must_use] + pub const fn start(&self) -> isize { + self.start + } + + #[must_use] + pub const fn stop(&self) -> isize { + self.stop + } + + #[must_use] + pub const fn step(&self) -> isize { + self.step + } + // Equivalent to PySlice_Unpack. pub fn with_slice(slice: &PySlice, vm: &VirtualMachine) -> PyResult { let step = to_isize_index(vm, slice.step_ref(vm))?.unwrap_or(1);