| 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 | |