Skip to content

Commit 2365c95

Browse files
committed
feat(capi): implement slice C-API methods
Implement missing Limited API / abi3 slice functions: - PySlice_Check - PySlice_GetIndices - PySlice_GetIndicesEx Assisted-by: Antigravity:Gemini-3.7-Flash
1 parent cb0f3b0 commit 2365c95

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

‎crates/capi/src/sliceobject.rs‎

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use crate::PyObject;
2+
use crate::object::define_py_check;
23
use crate::pystate::with_vm;
34
use crate::util::FfiPtrExt;
45
use core::ffi::c_int;
56
use rustpython_vm::PyPayload;
67
use rustpython_vm::builtins::PySlice;
78
use rustpython_vm::sliceable::SaturatedSlice;
89

10+
define_py_check!(fn PySlice_Check, types.slice_type);
11+
912
#[unsafe(no_mangle)]
1013
pub unsafe extern "C" fn PySlice_New(
1114
start: *mut PyObject,
@@ -62,6 +65,103 @@ pub unsafe extern "C" fn PySlice_AdjustIndices(
6265
slice_len as isize
6366
}
6467

68+
#[unsafe(no_mangle)]
69+
pub unsafe extern "C" fn PySlice_GetIndices(
70+
r: *mut PyObject,
71+
length: isize,
72+
start: *mut isize,
73+
stop: *mut isize,
74+
step: *mut isize,
75+
) -> c_int {
76+
with_vm(|vm| {
77+
let slice = unsafe { r.assume_borrowed_and_cast::<PySlice>(vm) }?;
78+
let step_val = if let Some(step) = &slice.step
79+
&& !vm.is_none(step)
80+
{
81+
let s: isize = step
82+
.try_index(vm)?
83+
.as_bigint()
84+
.try_into()
85+
.map_err(|_| vm.new_value_error("step out of range".to_owned()))?;
86+
if s == 0 {
87+
return Err(vm.new_value_error("slice step cannot be zero".to_owned()));
88+
}
89+
s
90+
} else {
91+
1
92+
};
93+
let start_val = if let Some(start) = &slice.start
94+
&& !vm.is_none(start)
95+
{
96+
let mut st: isize = start
97+
.try_index(vm)?
98+
.as_bigint()
99+
.try_into()
100+
.map_err(|_| vm.new_value_error("start out of range".to_owned()))?;
101+
if st < 0 {
102+
st += length;
103+
}
104+
st
105+
} else if step_val < 0 {
106+
length - 1
107+
} else {
108+
0
109+
};
110+
let stop_val = if !vm.is_none(&slice.stop) {
111+
let mut sp: isize = slice
112+
.stop
113+
.try_index(vm)?
114+
.as_bigint()
115+
.try_into()
116+
.map_err(|_| vm.new_value_error("stop out of range".to_owned()))?;
117+
if sp < 0 {
118+
sp += length;
119+
}
120+
sp
121+
} else if step_val < 0 {
122+
-1
123+
} else {
124+
length
125+
};
126+
if !start.is_null() {
127+
unsafe { *start = start_val };
128+
}
129+
if !stop.is_null() {
130+
unsafe { *stop = stop_val };
131+
}
132+
if !step.is_null() {
133+
unsafe { *step = step_val };
134+
}
135+
136+
if start_val > length || stop_val > length {
137+
return Ok(-1);
138+
}
139+
if step_val < 0 && (start_val < -1 || stop_val < -1) {
140+
return Ok(-1);
141+
}
142+
Ok(0)
143+
})
144+
}
145+
146+
#[unsafe(no_mangle)]
147+
pub unsafe extern "C" fn PySlice_GetIndicesEx(
148+
r: *mut PyObject,
149+
length: isize,
150+
start: *mut isize,
151+
stop: *mut isize,
152+
step: *mut isize,
153+
slicelength: *mut isize,
154+
) -> c_int {
155+
if unsafe { PySlice_Unpack(r, start, stop, step) } != 0 {
156+
return -1;
157+
}
158+
let len = unsafe { PySlice_AdjustIndices(length, start, stop, *step) };
159+
if !slicelength.is_null() {
160+
unsafe { *slicelength = len };
161+
}
162+
0
163+
}
164+
65165
#[cfg(test)]
66166
mod tests {
67167
use pyo3::prelude::*;
@@ -93,4 +193,60 @@ mod tests {
93193
assert_eq!((indices.start, indices.stop, indices.step), (10, 1, -2));
94194
})
95195
}
196+
197+
#[test]
198+
fn slice_get_indices_ex() {
199+
Python::attach(|py| {
200+
let slice = PySlice::new(py, 1, 5, 2);
201+
let mut start = 0;
202+
let mut stop = 0;
203+
let mut step = 0;
204+
let mut slicelen = 0;
205+
let ret = unsafe {
206+
super::PySlice_GetIndicesEx(
207+
slice.as_ptr().cast(),
208+
10,
209+
&mut start,
210+
&mut stop,
211+
&mut step,
212+
&mut slicelen,
213+
)
214+
};
215+
assert_eq!(ret, 0);
216+
assert_eq!((start, stop, step, slicelen), (1, 5, 2, 2));
217+
});
218+
}
219+
220+
#[test]
221+
fn slice_get_indices_bounds() {
222+
Python::attach(|py| {
223+
let slice_ok = PySlice::new(py, 1, 4, 1);
224+
let mut start = 0;
225+
let mut stop = 0;
226+
let mut step = 0;
227+
let ret = unsafe {
228+
super::PySlice_GetIndices(
229+
slice_ok.as_ptr().cast(),
230+
5,
231+
&mut start,
232+
&mut stop,
233+
&mut step,
234+
)
235+
};
236+
assert_eq!(ret, 0);
237+
assert_eq!((start, stop, step), (1, 4, 1));
238+
239+
let slice_out = PySlice::new(py, 1, 20, 1);
240+
let ret_out = unsafe {
241+
super::PySlice_GetIndices(
242+
slice_out.as_ptr().cast(),
243+
10,
244+
&mut start,
245+
&mut stop,
246+
&mut step,
247+
)
248+
};
249+
assert_eq!(ret_out, -1);
250+
});
251+
}
96252
}

0 commit comments

Comments
 (0)