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 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> for $ty { type Output = $ty; #[track_caller] fn index( &self, std::ops::Range { start, end }: std::ops::Range, ) -> &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> for $ty { type Output = $ty; #[track_caller] fn index( &self, std::ops::RangeFrom { start }: std::ops::RangeFrom, ) -> &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 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> for $ty { type Output = $ty; #[track_caller] fn index(&self, range: std::ops::RangeInclusive) -> &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> for $ty { type Output = $ty; #[track_caller] fn index(&self, std::ops::RangeTo { end }: std::ops::RangeTo) -> &Self::Output { &self[0..end] } } impl std::ops::Index> for $ty { type Output = $ty; #[track_caller] fn index( &self, std::ops::RangeToInclusive { end }: std::ops::RangeToInclusive, ) -> &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> { 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) }