1 | use crate::ffi_ptr_ext::FfiPtrExt; |
2 | use crate::instance::{Borrowed, Bound}; |
3 | use crate::types::any::PyAnyMethods; |
4 | use crate::{ffi, Py, PyAny, PyResult, Python}; |
5 | use std::ops::Index; |
6 | use std::slice::SliceIndex; |
7 | use std::str; |
8 | |
9 | /// Represents a Python `bytes` object. |
10 | /// |
11 | /// This type is immutable. |
12 | /// |
13 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as |
14 | /// [`Py<PyBytes>`][crate::Py] or [`Bound<'py, PyBytes>`][Bound]. |
15 | /// |
16 | /// For APIs available on `bytes` objects, see the [`PyBytesMethods`] trait which is implemented for |
17 | /// [`Bound<'py, PyBytes>`][Bound]. |
18 | /// |
19 | /// # Equality |
20 | /// |
21 | /// For convenience, [`Bound<'py, PyBytes>`][Bound] implements [`PartialEq<[u8]>`][PartialEq] to allow comparing the |
22 | /// data in the Python bytes to a Rust `[u8]` byte slice. |
23 | /// |
24 | /// This is not always the most appropriate way to compare Python bytes, as Python bytes subclasses |
25 | /// may have different equality semantics. In situations where subclasses overriding equality might be |
26 | /// relevant, use [`PyAnyMethods::eq`], at cost of the additional overhead of a Python method call. |
27 | /// |
28 | /// ```rust |
29 | /// # use pyo3::prelude::*; |
30 | /// use pyo3::types::PyBytes; |
31 | /// |
32 | /// # Python::with_gil(|py| { |
33 | /// let py_bytes = PyBytes::new(py, b"foo" .as_slice()); |
34 | /// // via PartialEq<[u8]> |
35 | /// assert_eq!(py_bytes, b"foo" .as_slice()); |
36 | /// |
37 | /// // via Python equality |
38 | /// let other = PyBytes::new(py, b"foo" .as_slice()); |
39 | /// assert!(py_bytes.as_any().eq(other).unwrap()); |
40 | /// |
41 | /// // Note that `eq` will convert its argument to Python using `IntoPyObject`. |
42 | /// // Byte collections are specialized, so that the following slice will indeed |
43 | /// // convert into a `bytes` object and not a `list`: |
44 | /// assert!(py_bytes.as_any().eq(b"foo" .as_slice()).unwrap()); |
45 | /// # }); |
46 | /// ``` |
47 | #[repr (transparent)] |
48 | pub struct PyBytes(PyAny); |
49 | |
50 | pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check); |
51 | |
52 | impl PyBytes { |
53 | /// Creates a new Python bytestring object. |
54 | /// The bytestring is initialized by copying the data from the `&[u8]`. |
55 | /// |
56 | /// Panics if out of memory. |
57 | pub fn new<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> { |
58 | let ptr = s.as_ptr().cast(); |
59 | let len = s.len() as ffi::Py_ssize_t; |
60 | unsafe { |
61 | ffi::PyBytes_FromStringAndSize(ptr, len) |
62 | .assume_owned(py) |
63 | .downcast_into_unchecked() |
64 | } |
65 | } |
66 | |
67 | /// Deprecated name for [`PyBytes::new`]. |
68 | #[deprecated (since = "0.23.0" , note = "renamed to `PyBytes::new`" )] |
69 | #[inline ] |
70 | pub fn new_bound<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> { |
71 | Self::new(py, s) |
72 | } |
73 | |
74 | /// Creates a new Python `bytes` object with an `init` closure to write its contents. |
75 | /// Before calling `init` the bytes' contents are zero-initialised. |
76 | /// * If Python raises a MemoryError on the allocation, `new_with` will return |
77 | /// it inside `Err`. |
78 | /// * If `init` returns `Err(e)`, `new_with` will return `Err(e)`. |
79 | /// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyBytes)`. |
80 | /// |
81 | /// # Examples |
82 | /// |
83 | /// ``` |
84 | /// use pyo3::{prelude::*, types::PyBytes}; |
85 | /// |
86 | /// # fn main() -> PyResult<()> { |
87 | /// Python::with_gil(|py| -> PyResult<()> { |
88 | /// let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| { |
89 | /// bytes.copy_from_slice(b"Hello Rust" ); |
90 | /// Ok(()) |
91 | /// })?; |
92 | /// let bytes: &[u8] = py_bytes.extract()?; |
93 | /// assert_eq!(bytes, b"Hello Rust" ); |
94 | /// Ok(()) |
95 | /// }) |
96 | /// # } |
97 | /// ``` |
98 | #[inline ] |
99 | pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>> |
100 | where |
101 | F: FnOnce(&mut [u8]) -> PyResult<()>, |
102 | { |
103 | unsafe { |
104 | let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t); |
105 | // Check for an allocation error and return it |
106 | let pybytes = pyptr.assume_owned_or_err(py)?.downcast_into_unchecked(); |
107 | let buffer: *mut u8 = ffi::PyBytes_AsString(pyptr).cast(); |
108 | debug_assert!(!buffer.is_null()); |
109 | // Zero-initialise the uninitialised bytestring |
110 | std::ptr::write_bytes(buffer, 0u8, len); |
111 | // (Further) Initialise the bytestring in init |
112 | // If init returns an Err, pypybytearray will automatically deallocate the buffer |
113 | init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pybytes) |
114 | } |
115 | } |
116 | |
117 | /// Deprecated name for [`PyBytes::new_with`]. |
118 | #[deprecated (since = "0.23.0" , note = "renamed to `PyBytes::new_with`" )] |
119 | #[inline ] |
120 | pub fn new_bound_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>> |
121 | where |
122 | F: FnOnce(&mut [u8]) -> PyResult<()>, |
123 | { |
124 | Self::new_with(py, len, init) |
125 | } |
126 | |
127 | /// Creates a new Python byte string object from a raw pointer and length. |
128 | /// |
129 | /// Panics if out of memory. |
130 | /// |
131 | /// # Safety |
132 | /// |
133 | /// This function dereferences the raw pointer `ptr` as the |
134 | /// leading pointer of a slice of length `len`. [As with |
135 | /// `std::slice::from_raw_parts`, this is |
136 | /// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety). |
137 | pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> { |
138 | unsafe { |
139 | ffi::PyBytes_FromStringAndSize(ptr.cast(), len as isize) |
140 | .assume_owned(py) |
141 | .downcast_into_unchecked() |
142 | } |
143 | } |
144 | |
145 | /// Deprecated name for [`PyBytes::from_ptr`]. |
146 | /// |
147 | /// # Safety |
148 | /// |
149 | /// This function dereferences the raw pointer `ptr` as the |
150 | /// leading pointer of a slice of length `len`. [As with |
151 | /// `std::slice::from_raw_parts`, this is |
152 | /// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety). |
153 | #[deprecated (since = "0.23.0" , note = "renamed to `PyBytes::from_ptr`" )] |
154 | #[inline ] |
155 | pub unsafe fn bound_from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> { |
156 | unsafe { Self::from_ptr(py, ptr, len) } |
157 | } |
158 | } |
159 | |
160 | /// Implementation of functionality for [`PyBytes`]. |
161 | /// |
162 | /// These methods are defined for the `Bound<'py, PyBytes>` smart pointer, so to use method call |
163 | /// syntax these methods are separated into a trait, because stable Rust does not yet support |
164 | /// `arbitrary_self_types`. |
165 | #[doc (alias = "PyBytes" )] |
166 | pub trait PyBytesMethods<'py>: crate::sealed::Sealed { |
167 | /// Gets the Python string as a byte slice. |
168 | fn as_bytes(&self) -> &[u8]; |
169 | } |
170 | |
171 | impl<'py> PyBytesMethods<'py> for Bound<'py, PyBytes> { |
172 | #[inline ] |
173 | fn as_bytes(&self) -> &[u8] { |
174 | self.as_borrowed().as_bytes() |
175 | } |
176 | } |
177 | |
178 | impl<'a> Borrowed<'a, '_, PyBytes> { |
179 | /// Gets the Python string as a byte slice. |
180 | #[allow (clippy::wrong_self_convention)] |
181 | pub(crate) fn as_bytes(self) -> &'a [u8] { |
182 | unsafe { |
183 | let buffer: *const u8 = ffi::PyBytes_AsString(self.as_ptr()) as *const u8; |
184 | let length: usize = ffi::PyBytes_Size(self.as_ptr()) as usize; |
185 | debug_assert!(!buffer.is_null()); |
186 | std::slice::from_raw_parts(data:buffer, len:length) |
187 | } |
188 | } |
189 | } |
190 | |
191 | impl Py<PyBytes> { |
192 | /// Gets the Python bytes as a byte slice. Because Python bytes are |
193 | /// immutable, the result may be used for as long as the reference to |
194 | /// `self` is held, including when the GIL is released. |
195 | pub fn as_bytes<'a>(&'a self, py: Python<'_>) -> &'a [u8] { |
196 | self.bind_borrowed(py).as_bytes() |
197 | } |
198 | } |
199 | |
200 | /// This is the same way [Vec] is indexed. |
201 | impl<I: SliceIndex<[u8]>> Index<I> for Bound<'_, PyBytes> { |
202 | type Output = I::Output; |
203 | |
204 | fn index(&self, index: I) -> &Self::Output { |
205 | &self.as_bytes()[index] |
206 | } |
207 | } |
208 | |
209 | /// Compares whether the Python bytes object is equal to the [u8]. |
210 | /// |
211 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
212 | impl PartialEq<[u8]> for Bound<'_, PyBytes> { |
213 | #[inline ] |
214 | fn eq(&self, other: &[u8]) -> bool { |
215 | self.as_borrowed() == *other |
216 | } |
217 | } |
218 | |
219 | /// Compares whether the Python bytes object is equal to the [u8]. |
220 | /// |
221 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
222 | impl PartialEq<&'_ [u8]> for Bound<'_, PyBytes> { |
223 | #[inline ] |
224 | fn eq(&self, other: &&[u8]) -> bool { |
225 | self.as_borrowed() == **other |
226 | } |
227 | } |
228 | |
229 | /// Compares whether the Python bytes object is equal to the [u8]. |
230 | /// |
231 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
232 | impl PartialEq<Bound<'_, PyBytes>> for [u8] { |
233 | #[inline ] |
234 | fn eq(&self, other: &Bound<'_, PyBytes>) -> bool { |
235 | *self == other.as_borrowed() |
236 | } |
237 | } |
238 | |
239 | /// Compares whether the Python bytes object is equal to the [u8]. |
240 | /// |
241 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
242 | impl PartialEq<&'_ Bound<'_, PyBytes>> for [u8] { |
243 | #[inline ] |
244 | fn eq(&self, other: &&Bound<'_, PyBytes>) -> bool { |
245 | *self == other.as_borrowed() |
246 | } |
247 | } |
248 | |
249 | /// Compares whether the Python bytes object is equal to the [u8]. |
250 | /// |
251 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
252 | impl PartialEq<Bound<'_, PyBytes>> for &'_ [u8] { |
253 | #[inline ] |
254 | fn eq(&self, other: &Bound<'_, PyBytes>) -> bool { |
255 | **self == other.as_borrowed() |
256 | } |
257 | } |
258 | |
259 | /// Compares whether the Python bytes object is equal to the [u8]. |
260 | /// |
261 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
262 | impl PartialEq<[u8]> for &'_ Bound<'_, PyBytes> { |
263 | #[inline ] |
264 | fn eq(&self, other: &[u8]) -> bool { |
265 | self.as_borrowed() == other |
266 | } |
267 | } |
268 | |
269 | /// Compares whether the Python bytes object is equal to the [u8]. |
270 | /// |
271 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
272 | impl PartialEq<[u8]> for Borrowed<'_, '_, PyBytes> { |
273 | #[inline ] |
274 | fn eq(&self, other: &[u8]) -> bool { |
275 | self.as_bytes() == other |
276 | } |
277 | } |
278 | |
279 | /// Compares whether the Python bytes object is equal to the [u8]. |
280 | /// |
281 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
282 | impl PartialEq<&[u8]> for Borrowed<'_, '_, PyBytes> { |
283 | #[inline ] |
284 | fn eq(&self, other: &&[u8]) -> bool { |
285 | *self == **other |
286 | } |
287 | } |
288 | |
289 | /// Compares whether the Python bytes object is equal to the [u8]. |
290 | /// |
291 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
292 | impl PartialEq<Borrowed<'_, '_, PyBytes>> for [u8] { |
293 | #[inline ] |
294 | fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool { |
295 | other == self |
296 | } |
297 | } |
298 | |
299 | /// Compares whether the Python bytes object is equal to the [u8]. |
300 | /// |
301 | /// In some cases Python equality might be more appropriate; see the note on [`PyBytes`]. |
302 | impl PartialEq<Borrowed<'_, '_, PyBytes>> for &'_ [u8] { |
303 | #[inline ] |
304 | fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool { |
305 | other == self |
306 | } |
307 | } |
308 | |
309 | #[cfg (test)] |
310 | mod tests { |
311 | use super::*; |
312 | |
313 | #[test ] |
314 | fn test_bytes_index() { |
315 | Python::with_gil(|py| { |
316 | let bytes = PyBytes::new(py, b"Hello World" ); |
317 | assert_eq!(bytes[1], b'e' ); |
318 | }); |
319 | } |
320 | |
321 | #[test ] |
322 | fn test_bound_bytes_index() { |
323 | Python::with_gil(|py| { |
324 | let bytes = PyBytes::new(py, b"Hello World" ); |
325 | assert_eq!(bytes[1], b'e' ); |
326 | |
327 | let bytes = &bytes; |
328 | assert_eq!(bytes[1], b'e' ); |
329 | }); |
330 | } |
331 | |
332 | #[test ] |
333 | fn test_bytes_new_with() -> super::PyResult<()> { |
334 | Python::with_gil(|py| -> super::PyResult<()> { |
335 | let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| { |
336 | b.copy_from_slice(b"Hello Rust" ); |
337 | Ok(()) |
338 | })?; |
339 | let bytes: &[u8] = py_bytes.extract()?; |
340 | assert_eq!(bytes, b"Hello Rust" ); |
341 | Ok(()) |
342 | }) |
343 | } |
344 | |
345 | #[test ] |
346 | fn test_bytes_new_with_zero_initialised() -> super::PyResult<()> { |
347 | Python::with_gil(|py| -> super::PyResult<()> { |
348 | let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| Ok(()))?; |
349 | let bytes: &[u8] = py_bytes.extract()?; |
350 | assert_eq!(bytes, &[0; 10]); |
351 | Ok(()) |
352 | }) |
353 | } |
354 | |
355 | #[test ] |
356 | fn test_bytes_new_with_error() { |
357 | use crate::exceptions::PyValueError; |
358 | Python::with_gil(|py| { |
359 | let py_bytes_result = PyBytes::new_with(py, 10, |_b: &mut [u8]| { |
360 | Err(PyValueError::new_err("Hello Crustaceans!" )) |
361 | }); |
362 | assert!(py_bytes_result.is_err()); |
363 | assert!(py_bytes_result |
364 | .err() |
365 | .unwrap() |
366 | .is_instance_of::<PyValueError>(py)); |
367 | }); |
368 | } |
369 | |
370 | #[test ] |
371 | fn test_comparisons() { |
372 | Python::with_gil(|py| { |
373 | let b = b"hello, world" .as_slice(); |
374 | let py_bytes = PyBytes::new(py, b); |
375 | |
376 | assert_eq!(py_bytes, b"hello, world" .as_slice()); |
377 | |
378 | assert_eq!(py_bytes, b); |
379 | assert_eq!(&py_bytes, b); |
380 | assert_eq!(b, py_bytes); |
381 | assert_eq!(b, &py_bytes); |
382 | |
383 | assert_eq!(py_bytes, *b); |
384 | assert_eq!(&py_bytes, *b); |
385 | assert_eq!(*b, py_bytes); |
386 | assert_eq!(*b, &py_bytes); |
387 | |
388 | let py_string = py_bytes.as_borrowed(); |
389 | |
390 | assert_eq!(py_string, b); |
391 | assert_eq!(&py_string, b); |
392 | assert_eq!(b, py_string); |
393 | assert_eq!(b, &py_string); |
394 | |
395 | assert_eq!(py_string, *b); |
396 | assert_eq!(*b, py_string); |
397 | }) |
398 | } |
399 | } |
400 | |