use pyo3::exceptions::{PyIndexError, PyOverflowError};
use pyo3::PyErr;
pub(super) fn py_long_to_valid_index(
value: pyo3::Bound<'_, pyo3::types::PyInt>,
elements_count: usize,
) -> pyo3::PyResult<usize> {
use pyo3::types::PyAnyMethods;
if let Ok(index) = value.extract::<isize>() {
let elements_count = elements_count as isize;
if !(-elements_count <= index && index < elements_count) {
Err(PyIndexError::new_err(format!(
"Index {} is out of range({}, {}).",
index, -elements_count, elements_count
)))
} else {
Ok((if index < 0 {
index + elements_count
} else {
index
}) as usize)
}
} else {
Err(PyIndexError::new_err(format!(
"Index {} is out of index integer range({}, {}).",
value.repr()?,
isize::MIN,
(isize::MAX as usize) + 1
)))
}
}
pub(super) fn normalize_index_start(
start: Option<&pyo3::Bound<'_, pyo3::types::PyInt>>,
elements_count: usize,
) -> usize {
use pyo3::types::PyAnyMethods;
start
.map(|value| {
value
.extract::<isize>()
.map(|value| {
(if value < 0 {
(value + (elements_count as isize)).max(0)
} else {
value
}) as usize
})
.unwrap_or(elements_count)
})
.unwrap_or(0usize)
}
pub(super) fn normalize_index_stop(
start: Option<&pyo3::Bound<'_, pyo3::types::PyInt>>,
elements_count: usize,
) -> usize {
use pyo3::types::PyAnyMethods;
start
.map(|value| {
value
.extract::<isize>()
.map(|value| {
(if value < 0 {
(value + (elements_count as isize)).max(0)
} else {
value
}) as usize
})
.unwrap_or(0)
})
.unwrap_or(elements_count)
}
pub(super) fn to_next_slice_indices(
start: isize,
step: isize,
length: usize,
slice: pyo3::Bound<'_, pyo3::types::PySlice>,
) -> Result<(isize, isize, isize), PyErr> {
use pyo3::types::PySliceMethods;
let indices = slice.indices(length as isize)?;
let result_step = try_multiply_isizes(step, indices.step)?;
let result_start =
try_sum_isizes(start, try_multiply_isizes(step, indices.start)?)?;
let result_stop =
try_sum_isizes(start, try_multiply_isizes(step, indices.stop)?)?;
Ok((result_start, result_stop, result_step))
}
fn try_multiply_isizes(first: isize, second: isize) -> pyo3::PyResult<isize> {
if let (result, false) = first.overflowing_mul(second) {
Ok(result)
} else {
Err(PyOverflowError::new_err(format!(
"Multiplication of {} & {} is out of range({}, {}).",
first,
second,
isize::MIN,
(isize::MAX as usize) + 1,
)))
}
}
fn try_sum_isizes(first: isize, second: isize) -> pyo3::PyResult<isize> {
if let (result, false) = first.overflowing_add(second) {
Ok(result)
} else {
Err(PyOverflowError::new_err(format!(
"Addition of {} & {} is out of range({}, {}).",
first,
second,
isize::MIN,
(isize::MAX as usize) + 1,
)))
}
}