|
1 | 1 | use crate::PyObject; |
| 2 | +use crate::object::define_py_check; |
2 | 3 | use crate::pystate::with_vm; |
3 | 4 | use crate::util::FfiPtrExt; |
4 | 5 | use core::ffi::c_int; |
5 | 6 | use rustpython_vm::PyPayload; |
6 | 7 | use rustpython_vm::builtins::PySlice; |
7 | 8 | use rustpython_vm::sliceable::SaturatedSlice; |
8 | 9 |
|
| 10 | +define_py_check!(fn PySlice_Check, types.slice_type); |
| 11 | + |
9 | 12 | #[unsafe(no_mangle)] |
10 | 13 | pub unsafe extern "C" fn PySlice_New( |
11 | 14 | start: *mut PyObject, |
@@ -62,6 +65,103 @@ pub unsafe extern "C" fn PySlice_AdjustIndices( |
62 | 65 | slice_len as isize |
63 | 66 | } |
64 | 67 |
|
| 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 | + |
65 | 165 | #[cfg(test)] |
66 | 166 | mod tests { |
67 | 167 | use pyo3::prelude::*; |
@@ -93,4 +193,60 @@ mod tests { |
93 | 193 | assert_eq!((indices.start, indices.stop, indices.step), (10, 1, -2)); |
94 | 194 | }) |
95 | 195 | } |
| 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 | + } |
96 | 252 | } |
0 commit comments