1 | use std::{ |
2 | borrow::Cow, |
3 | ffi::{CStr, CString}, |
4 | }; |
5 | |
6 | use crate::{ |
7 | exceptions::PyValueError, |
8 | ffi::{Py_ssize_t, PY_SSIZE_T_MAX}, |
9 | PyResult, |
10 | }; |
11 | pub struct PrivateMarker; |
12 | |
13 | macro_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 | |
21 | macro_rules! private_impl { |
22 | () => { |
23 | fn __private__(&self) -> crate::internal_tricks::PrivateMarker { |
24 | crate::internal_tricks::PrivateMarker |
25 | } |
26 | }; |
27 | } |
28 | |
29 | macro_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. |
44 | pub(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 |
49 | macro_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 ] |
157 | pub(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 ] |
167 | pub(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 ] |
177 | pub(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 ] |
187 | pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! { |
188 | panic!("slice index starts at {} but ends at {}" , index, end); |
189 | } |
190 | |
191 | pub(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 | |