use crate::byteswriter::PyBytesWriter;
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::{Borrowed, Bound};
use crate::{ffi, Py, PyAny, PyResult, Python};
use std::io::Write;
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), "builtins", "bytes", #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)
.cast_into_unchecked()
}
}
#[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)?.cast_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)
}
}
#[inline]
pub fn new_with_writer<F>(
py: Python<'_>,
reserved_capacity: usize,
write: F,
) -> PyResult<Bound<'_, PyBytes>>
where
F: FnOnce(&mut dyn Write) -> PyResult<()>,
{
let mut writer = PyBytesWriter::with_capacity(py, reserved_capacity)?;
write(&mut writer)?;
writer.try_into()
}
pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
unsafe {
ffi::PyBytes_FromStringAndSize(ptr.cast(), len as isize)
.assume_owned(py)
.cast_into_unchecked()
}
}
}
#[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
}
}
impl<'a> AsRef<[u8]> for Borrowed<'a, '_, PyBytes> {
#[inline]
fn as_ref(&self) -> &'a [u8] {
self.as_bytes()
}
}
impl AsRef<[u8]> for Bound<'_, PyBytes> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::PyAnyMethods as _;
#[test]
fn test_bytes_index() {
Python::attach(|py| {
let bytes = PyBytes::new(py, b"Hello World");
assert_eq!(bytes[1], b'e');
});
}
#[test]
fn test_bound_bytes_index() {
Python::attach(|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::attach(|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::attach(|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::attach(|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::attach(|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);
})
}
#[test]
#[cfg(not(Py_LIMITED_API))]
fn test_as_string() {
Python::attach(|py| {
let b = b"hello, world".as_slice();
let py_bytes = PyBytes::new(py, b);
unsafe {
assert_eq!(
ffi::PyBytes_AsString(py_bytes.as_ptr()) as *const std::ffi::c_char,
ffi::PyBytes_AS_STRING(py_bytes.as_ptr()) as *const std::ffi::c_char
);
}
})
}
#[test]
fn test_as_ref_slice() {
Python::attach(|py| {
let b = b"hello, world";
let py_bytes = PyBytes::new(py, b);
let ref_bound: &[u8] = py_bytes.as_ref();
assert_eq!(ref_bound, b);
let py_bytes_borrowed = py_bytes.as_borrowed();
let ref_borrowed: &[u8] = py_bytes_borrowed.as_ref();
assert_eq!(ref_borrowed, b);
})
}
#[test]
fn test_with_writer() {
Python::attach(|py| {
let bytes = PyBytes::new_with_writer(py, 0, |writer| {
writer.write_all(b"hallo")?;
Ok(())
})
.unwrap();
assert_eq!(bytes.as_bytes(), b"hallo");
})
}
}