use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::{Borrowed, Bound};
use crate::types::any::PyAnyMethods;
use crate::{ffi, Py, PyAny, PyResult, Python};
use std::ops::Index;
use std::slice::SliceIndex;
use std::str;
#[repr(transparent)]
pub struct PyBytes(PyAny);
pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check);
impl PyBytes {
pub fn new<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> {
let ptr = s.as_ptr().cast();
let len = s.len() as ffi::Py_ssize_t;
unsafe {
ffi::PyBytes_FromStringAndSize(ptr, len)
.assume_owned(py)
.downcast_into_unchecked()
}
}
#[deprecated(since = "0.23.0", note = "renamed to `PyBytes::new`")]
#[inline]
pub fn new_bound<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> {
Self::new(py, s)
}
#[inline]
pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>>
where
F: FnOnce(&mut [u8]) -> PyResult<()>,
{
unsafe {
let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t);
let pybytes = pyptr.assume_owned_or_err(py)?.downcast_into_unchecked();
let buffer: *mut u8 = ffi::PyBytes_AsString(pyptr).cast();
debug_assert!(!buffer.is_null());
std::ptr::write_bytes(buffer, 0u8, len);
init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pybytes)
}
}
#[deprecated(since = "0.23.0", note = "renamed to `PyBytes::new_with`")]
#[inline]
pub fn new_bound_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>>
where
F: FnOnce(&mut [u8]) -> PyResult<()>,
{
Self::new_with(py, len, init)
}
pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
ffi::PyBytes_FromStringAndSize(ptr.cast(), len as isize)
.assume_owned(py)
.downcast_into_unchecked()
}
#[deprecated(since = "0.23.0", note = "renamed to `PyBytes::from_ptr`")]
#[inline]
pub unsafe fn bound_from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
Self::from_ptr(py, ptr, len)
}
}
#[doc(alias = "PyBytes")]
pub trait PyBytesMethods<'py>: crate::sealed::Sealed {
fn as_bytes(&self) -> &[u8];
}
impl<'py> PyBytesMethods<'py> for Bound<'py, PyBytes> {
#[inline]
fn as_bytes(&self) -> &[u8] {
self.as_borrowed().as_bytes()
}
}
impl<'a> Borrowed<'a, '_, PyBytes> {
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_bytes(self) -> &'a [u8] {
unsafe {
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
let length = ffi::PyBytes_Size(self.as_ptr()) as usize;
debug_assert!(!buffer.is_null());
std::slice::from_raw_parts(buffer, length)
}
}
}
impl Py<PyBytes> {
pub fn as_bytes<'a>(&'a self, py: Python<'_>) -> &'a [u8] {
self.bind_borrowed(py).as_bytes()
}
}
impl<I: SliceIndex<[u8]>> Index<I> for Bound<'_, PyBytes> {
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.as_bytes()[index]
}
}
impl PartialEq<[u8]> for Bound<'_, PyBytes> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.as_borrowed() == *other
}
}
impl PartialEq<&'_ [u8]> for Bound<'_, PyBytes> {
#[inline]
fn eq(&self, other: &&[u8]) -> bool {
self.as_borrowed() == **other
}
}
impl PartialEq<Bound<'_, PyBytes>> for [u8] {
#[inline]
fn eq(&self, other: &Bound<'_, PyBytes>) -> bool {
*self == other.as_borrowed()
}
}
impl PartialEq<&'_ Bound<'_, PyBytes>> for [u8] {
#[inline]
fn eq(&self, other: &&Bound<'_, PyBytes>) -> bool {
*self == other.as_borrowed()
}
}
impl PartialEq<Bound<'_, PyBytes>> for &'_ [u8] {
#[inline]
fn eq(&self, other: &Bound<'_, PyBytes>) -> bool {
**self == other.as_borrowed()
}
}
impl PartialEq<[u8]> for &'_ Bound<'_, PyBytes> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.as_borrowed() == other
}
}
impl PartialEq<[u8]> for Borrowed<'_, '_, PyBytes> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.as_bytes() == other
}
}
impl PartialEq<&[u8]> for Borrowed<'_, '_, PyBytes> {
#[inline]
fn eq(&self, other: &&[u8]) -> bool {
*self == **other
}
}
impl PartialEq<Borrowed<'_, '_, PyBytes>> for [u8] {
#[inline]
fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool {
other == self
}
}
impl PartialEq<Borrowed<'_, '_, PyBytes>> for &'_ [u8] {
#[inline]
fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool {
other == self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bytes_index() {
Python::with_gil(|py| {
let bytes = PyBytes::new(py, b"Hello World");
assert_eq!(bytes[1], b'e');
});
}
#[test]
fn test_bound_bytes_index() {
Python::with_gil(|py| {
let bytes = PyBytes::new(py, b"Hello World");
assert_eq!(bytes[1], b'e');
let bytes = &bytes;
assert_eq!(bytes[1], b'e');
});
}
#[test]
fn test_bytes_new_with() -> super::PyResult<()> {
Python::with_gil(|py| -> super::PyResult<()> {
let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| {
b.copy_from_slice(b"Hello Rust");
Ok(())
})?;
let bytes: &[u8] = py_bytes.extract()?;
assert_eq!(bytes, b"Hello Rust");
Ok(())
})
}
#[test]
fn test_bytes_new_with_zero_initialised() -> super::PyResult<()> {
Python::with_gil(|py| -> super::PyResult<()> {
let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| Ok(()))?;
let bytes: &[u8] = py_bytes.extract()?;
assert_eq!(bytes, &[0; 10]);
Ok(())
})
}
#[test]
fn test_bytes_new_with_error() {
use crate::exceptions::PyValueError;
Python::with_gil(|py| {
let py_bytes_result = PyBytes::new_with(py, 10, |_b: &mut [u8]| {
Err(PyValueError::new_err("Hello Crustaceans!"))
});
assert!(py_bytes_result.is_err());
assert!(py_bytes_result
.err()
.unwrap()
.is_instance_of::<PyValueError>(py));
});
}
#[test]
fn test_comparisons() {
Python::with_gil(|py| {
let b = b"hello, world".as_slice();
let py_bytes = PyBytes::new(py, b);
assert_eq!(py_bytes, b"hello, world".as_slice());
assert_eq!(py_bytes, b);
assert_eq!(&py_bytes, b);
assert_eq!(b, py_bytes);
assert_eq!(b, &py_bytes);
assert_eq!(py_bytes, *b);
assert_eq!(&py_bytes, *b);
assert_eq!(*b, py_bytes);
assert_eq!(*b, &py_bytes);
let py_string = py_bytes.as_borrowed();
assert_eq!(py_string, b);
assert_eq!(&py_string, b);
assert_eq!(b, py_string);
assert_eq!(b, &py_string);
assert_eq!(py_string, *b);
assert_eq!(*b, py_string);
})
}
}