215 lines
6.2 KiB
Rust
215 lines
6.2 KiB
Rust
use std::{
|
|
borrow::Cow,
|
|
ffi::{CStr, CString},
|
|
};
|
|
|
|
use crate::{
|
|
exceptions::PyValueError,
|
|
ffi::{Py_ssize_t, PY_SSIZE_T_MAX},
|
|
PyResult,
|
|
};
|
|
pub struct PrivateMarker;
|
|
|
|
macro_rules! private_decl {
|
|
() => {
|
|
/// This trait is private to implement; this method exists to make it
|
|
/// impossible to implement outside the crate.
|
|
fn __private__(&self) -> crate::internal_tricks::PrivateMarker;
|
|
};
|
|
}
|
|
|
|
macro_rules! private_impl {
|
|
() => {
|
|
fn __private__(&self) -> crate::internal_tricks::PrivateMarker {
|
|
crate::internal_tricks::PrivateMarker
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! pyo3_exception {
|
|
($doc: expr, $name: ident, $base: ty) => {
|
|
#[doc = $doc]
|
|
#[repr(transparent)]
|
|
#[allow(non_camel_case_types)]
|
|
pub struct $name($crate::PyAny);
|
|
|
|
$crate::impl_exception_boilerplate!($name);
|
|
|
|
$crate::create_exception_type_object!(pyo3_runtime, $name, $base, Some($doc));
|
|
};
|
|
}
|
|
|
|
/// Convert an usize index into a Py_ssize_t index, clamping overflow to
|
|
/// PY_SSIZE_T_MAX.
|
|
pub(crate) fn get_ssize_index(index: usize) -> Py_ssize_t {
|
|
index.min(PY_SSIZE_T_MAX as usize) as Py_ssize_t
|
|
}
|
|
|
|
/// Implementations used for slice indexing PySequence, PyTuple, and PyList
|
|
macro_rules! index_impls {
|
|
(
|
|
$ty:ty,
|
|
$ty_name:literal,
|
|
$len:expr,
|
|
$get_slice:expr $(,)?
|
|
) => {
|
|
impl std::ops::Index<usize> for $ty {
|
|
// Always PyAny output (even if the slice operation returns something else)
|
|
type Output = PyAny;
|
|
|
|
#[track_caller]
|
|
fn index(&self, index: usize) -> &Self::Output {
|
|
self.get_item(index).unwrap_or_else(|_| {
|
|
crate::internal_tricks::index_len_fail(index, $ty_name, $len(self))
|
|
})
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::Range<usize>> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(
|
|
&self,
|
|
std::ops::Range { start, end }: std::ops::Range<usize>,
|
|
) -> &Self::Output {
|
|
let len = $len(self);
|
|
if start > len {
|
|
crate::internal_tricks::slice_start_index_len_fail(start, $ty_name, len)
|
|
} else if end > len {
|
|
crate::internal_tricks::slice_end_index_len_fail(end, $ty_name, len)
|
|
} else if start > end {
|
|
crate::internal_tricks::slice_index_order_fail(start, end)
|
|
} else {
|
|
$get_slice(self, start, end)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::RangeFrom<usize>> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(
|
|
&self,
|
|
std::ops::RangeFrom { start }: std::ops::RangeFrom<usize>,
|
|
) -> &Self::Output {
|
|
let len = $len(self);
|
|
if start > len {
|
|
crate::internal_tricks::slice_start_index_len_fail(start, $ty_name, len)
|
|
} else {
|
|
$get_slice(self, start, len)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::RangeFull> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(&self, _: std::ops::RangeFull) -> &Self::Output {
|
|
let len = $len(self);
|
|
$get_slice(self, 0, len)
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::RangeInclusive<usize>> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(&self, range: std::ops::RangeInclusive<usize>) -> &Self::Output {
|
|
let exclusive_end = range
|
|
.end()
|
|
.checked_add(1)
|
|
.expect("range end exceeds Python limit");
|
|
&self[*range.start()..exclusive_end]
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::RangeTo<usize>> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(&self, std::ops::RangeTo { end }: std::ops::RangeTo<usize>) -> &Self::Output {
|
|
&self[0..end]
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<std::ops::RangeToInclusive<usize>> for $ty {
|
|
type Output = $ty;
|
|
|
|
#[track_caller]
|
|
fn index(
|
|
&self,
|
|
std::ops::RangeToInclusive { end }: std::ops::RangeToInclusive<usize>,
|
|
) -> &Self::Output {
|
|
&self[0..=end]
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// these error messages are shamelessly "borrowed" from std.
|
|
|
|
#[inline(never)]
|
|
#[cold]
|
|
#[track_caller]
|
|
pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
|
|
panic!(
|
|
"index {} out of range for {} of length {}",
|
|
index, ty_name, len
|
|
);
|
|
}
|
|
|
|
#[inline(never)]
|
|
#[cold]
|
|
#[track_caller]
|
|
pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
|
|
panic!(
|
|
"range start index {} out of range for {} of length {}",
|
|
index, ty_name, len
|
|
);
|
|
}
|
|
|
|
#[inline(never)]
|
|
#[cold]
|
|
#[track_caller]
|
|
pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
|
|
panic!(
|
|
"range end index {} out of range for {} of length {}",
|
|
index, ty_name, len
|
|
);
|
|
}
|
|
|
|
#[inline(never)]
|
|
#[cold]
|
|
#[track_caller]
|
|
pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
|
panic!("slice index starts at {} but ends at {}", index, end);
|
|
}
|
|
|
|
pub(crate) fn extract_c_string(
|
|
src: &'static str,
|
|
err_msg: &'static str,
|
|
) -> PyResult<Cow<'static, CStr>> {
|
|
let bytes = src.as_bytes();
|
|
let cow = match bytes {
|
|
[] => {
|
|
// Empty string, we can trivially refer to a static "\0" string
|
|
Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") })
|
|
}
|
|
[.., 0] => {
|
|
// Last byte is a nul; try to create as a CStr
|
|
let c_str =
|
|
CStr::from_bytes_with_nul(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
|
Cow::Borrowed(c_str)
|
|
}
|
|
_ => {
|
|
// Allocate a new CString for this
|
|
let c_string = CString::new(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
|
Cow::Owned(c_string)
|
|
}
|
|
};
|
|
Ok(cow)
|
|
}
|