1use std::{
2 borrow::Cow,
3 ffi::{CStr, CString},
4};
5
6use crate::{
7 exceptions::PyValueError,
8 ffi::{Py_ssize_t, PY_SSIZE_T_MAX},
9 PyResult,
10};
11pub struct PrivateMarker;
12
13macro_rules! private_decl {
14 () => {
15 /// This trait is private to implement; this method exists to make it
16 /// impossible to implement outside the crate.
17 fn __private__(&self) -> crate::internal_tricks::PrivateMarker;
18 };
19}
20
21macro_rules! private_impl {
22 () => {
23 fn __private__(&self) -> crate::internal_tricks::PrivateMarker {
24 crate::internal_tricks::PrivateMarker
25 }
26 };
27}
28
29macro_rules! pyo3_exception {
30 ($doc: expr, $name: ident, $base: ty) => {
31 #[doc = $doc]
32 #[repr(transparent)]
33 #[allow(non_camel_case_types)]
34 pub struct $name($crate::PyAny);
35
36 $crate::impl_exception_boilerplate!($name);
37
38 $crate::create_exception_type_object!(pyo3_runtime, $name, $base, Some($doc));
39 };
40}
41
42/// Convert an usize index into a Py_ssize_t index, clamping overflow to
43/// PY_SSIZE_T_MAX.
44pub(crate) fn get_ssize_index(index: usize) -> Py_ssize_t {
45 index.min(PY_SSIZE_T_MAX as usize) as Py_ssize_t
46}
47
48/// Implementations used for slice indexing PySequence, PyTuple, and PyList
49macro_rules! index_impls {
50 (
51 $ty:ty,
52 $ty_name:literal,
53 $len:expr,
54 $get_slice:expr $(,)?
55 ) => {
56 impl std::ops::Index<usize> for $ty {
57 // Always PyAny output (even if the slice operation returns something else)
58 type Output = PyAny;
59
60 #[track_caller]
61 fn index(&self, index: usize) -> &Self::Output {
62 self.get_item(index).unwrap_or_else(|_| {
63 crate::internal_tricks::index_len_fail(index, $ty_name, $len(self))
64 })
65 }
66 }
67
68 impl std::ops::Index<std::ops::Range<usize>> for $ty {
69 type Output = $ty;
70
71 #[track_caller]
72 fn index(
73 &self,
74 std::ops::Range { start, end }: std::ops::Range<usize>,
75 ) -> &Self::Output {
76 let len = $len(self);
77 if start > len {
78 crate::internal_tricks::slice_start_index_len_fail(start, $ty_name, len)
79 } else if end > len {
80 crate::internal_tricks::slice_end_index_len_fail(end, $ty_name, len)
81 } else if start > end {
82 crate::internal_tricks::slice_index_order_fail(start, end)
83 } else {
84 $get_slice(self, start, end)
85 }
86 }
87 }
88
89 impl std::ops::Index<std::ops::RangeFrom<usize>> for $ty {
90 type Output = $ty;
91
92 #[track_caller]
93 fn index(
94 &self,
95 std::ops::RangeFrom { start }: std::ops::RangeFrom<usize>,
96 ) -> &Self::Output {
97 let len = $len(self);
98 if start > len {
99 crate::internal_tricks::slice_start_index_len_fail(start, $ty_name, len)
100 } else {
101 $get_slice(self, start, len)
102 }
103 }
104 }
105
106 impl std::ops::Index<std::ops::RangeFull> for $ty {
107 type Output = $ty;
108
109 #[track_caller]
110 fn index(&self, _: std::ops::RangeFull) -> &Self::Output {
111 let len = $len(self);
112 $get_slice(self, 0, len)
113 }
114 }
115
116 impl std::ops::Index<std::ops::RangeInclusive<usize>> for $ty {
117 type Output = $ty;
118
119 #[track_caller]
120 fn index(&self, range: std::ops::RangeInclusive<usize>) -> &Self::Output {
121 let exclusive_end = range
122 .end()
123 .checked_add(1)
124 .expect("range end exceeds Python limit");
125 &self[*range.start()..exclusive_end]
126 }
127 }
128
129 impl std::ops::Index<std::ops::RangeTo<usize>> for $ty {
130 type Output = $ty;
131
132 #[track_caller]
133 fn index(&self, std::ops::RangeTo { end }: std::ops::RangeTo<usize>) -> &Self::Output {
134 &self[0..end]
135 }
136 }
137
138 impl std::ops::Index<std::ops::RangeToInclusive<usize>> for $ty {
139 type Output = $ty;
140
141 #[track_caller]
142 fn index(
143 &self,
144 std::ops::RangeToInclusive { end }: std::ops::RangeToInclusive<usize>,
145 ) -> &Self::Output {
146 &self[0..=end]
147 }
148 }
149 };
150}
151
152// these error messages are shamelessly "borrowed" from std.
153
154#[inline(never)]
155#[cold]
156#[track_caller]
157pub(crate) fn index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
158 panic!(
159 "index {} out of range for {} of length {}",
160 index, ty_name, len
161 );
162}
163
164#[inline(never)]
165#[cold]
166#[track_caller]
167pub(crate) fn slice_start_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
168 panic!(
169 "range start index {} out of range for {} of length {}",
170 index, ty_name, len
171 );
172}
173
174#[inline(never)]
175#[cold]
176#[track_caller]
177pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize) -> ! {
178 panic!(
179 "range end index {} out of range for {} of length {}",
180 index, ty_name, len
181 );
182}
183
184#[inline(never)]
185#[cold]
186#[track_caller]
187pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
188 panic!("slice index starts at {} but ends at {}", index, end);
189}
190
191pub(crate) fn extract_c_string(
192 src: &'static str,
193 err_msg: &'static str,
194) -> PyResult<Cow<'static, CStr>> {
195 let bytes: &[u8] = src.as_bytes();
196 let cow: Cow<'_, CStr> = match bytes {
197 [] => {
198 // Empty string, we can trivially refer to a static "\0" string
199 Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(bytes:b"\0") })
200 }
201 [.., 0] => {
202 // Last byte is a nul; try to create as a CStr
203 let c_str: &CStr =
204 CStr::from_bytes_with_nul(bytes).map_err(|_| PyValueError::new_err(args:err_msg))?;
205 Cow::Borrowed(c_str)
206 }
207 _ => {
208 // Allocate a new CString for this
209 let c_string: CString = CString::new(bytes).map_err(|_| PyValueError::new_err(args:err_msg))?;
210 Cow::Owned(c_string)
211 }
212 };
213 Ok(cow)
214}
215