Skip to content

Commit

Permalink
Added examples to PyBytes::new_with and PyBytes::new_with_uninit (now…
Browse files Browse the repository at this point in the history
… with MaybeUninit<u8>)
  • Loading branch information
juntyr committed Aug 2, 2020
1 parent 20d27ff commit 533c105
Showing 1 changed file with 61 additions and 15 deletions.
76 changes: 61 additions & 15 deletions src/types/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyObject, PyResult, PyTryFrom, Python,
ToPyObject,
};
use std::mem::MaybeUninit;
use std::ops::Index;
use std::os::raw::c_char;
use std::slice::SliceIndex;
Expand Down Expand Up @@ -30,34 +31,43 @@ impl PyBytes {
/// The bytestring is uninitialised and must not be read.
///
/// Panics if out of memory.
unsafe fn new_with_uninit_impl(py: Python<'_>, len: usize) -> (&mut [u8], &PyBytes) {
#[inline]
unsafe fn new_with_uninit_impl(
py: Python<'_>,
len: usize,
) -> (&mut [MaybeUninit<u8>], &PyBytes) {
let length = len as ffi::Py_ssize_t;
let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), length);
// Iff pyptr is null, py.from_owned_ptr(pyptr) will panic
let pybytes = py.from_owned_ptr(pyptr);
let buffer = ffi::PyBytes_AsString(pyptr) as *mut u8;
let buffer = ffi::PyBytes_AsString(pyptr) as *mut MaybeUninit<u8>;
debug_assert!(!buffer.is_null());
let slice = std::slice::from_raw_parts_mut(buffer, len);
(slice, pybytes)
}

/// Creates a new Python bytestring object.
/// The `init` closure can initialise the bytestring.
///
/// # Safety
/// * The bytestring is zero-initialised and can be read inside `init`.
/// The bytestring is zero-initialised and can be read inside `init`.
/// The `init` closure can further initialise the bytestring.
///
/// Panics if out of memory.
///
/// # Example
/// ```
/// use crate::prelude::*;
/// Python::with_gil(|py| -> PyResult<()> {
/// let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| {
/// bytes.copy_from_slice(b"Hello Rust");
/// });
/// let bytes: &[u8] = FromPyObject::extract(py_bytes)?;
/// assert_eq!(bytes, b"Hello Rust");
/// Ok(())
/// });
/// ```
pub fn new_with<F: Fn(&mut [u8])>(py: Python<'_>, len: usize, init: F) -> &PyBytes {
let (slice, pybytes) = unsafe { Self::new_with_uninit_impl(py, len) };
#[cfg(feature = "slice_fill")]
{
slice.fill(0);
}
#[cfg(not(feature = "slice_fill"))]
{
slice.iter_mut().for_each(|x| *x = 0);
}
slice.iter_mut().for_each(|x| *x = MaybeUninit::zeroed());
let slice: &mut [u8] = unsafe { &mut *(slice as *mut [MaybeUninit<u8>] as *mut [u8]) };
init(slice);
pybytes
}
Expand All @@ -70,7 +80,25 @@ impl PyBytes {
/// follows does not read uninitialised memory.
///
/// Panics if out of memory.
pub unsafe fn new_with_uninit<F: Fn(&mut [u8])>(
///
/// # Example
/// ```
/// use pyo3::prelude::*;
/// use std::mem::MaybeUninit;
/// Python::with_gil(|py| -> PyResult<()> {
/// let py_bytes =
/// unsafe { PyBytes::new_with_uninit(py, 10, |uninit_bytes: &mut [MaybeUninit<u8>]| {
/// uninit_bytes
/// .iter_mut()
/// .zip(b"Hello Rust".into_iter())
/// .for_each(|(ub, b)| *ub = MaybeUninit::new(*b));
/// }) };
/// let bytes: &[u8] = FromPyObject::extract(py_bytes)?;
/// assert_eq!(bytes, b"Hello Rust");
/// Ok(())
/// });
/// ```
pub unsafe fn new_with_uninit<F: Fn(&mut [MaybeUninit<u8>])>(
py: Python<'_>,
len: usize,
init: F,
Expand Down Expand Up @@ -165,4 +193,22 @@ mod test {
let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap();
assert_eq!(bytes, &[0; 10]);
}

#[test]
fn test_bytes_new_uninit() {
use std::mem::MaybeUninit;

let gil = Python::acquire_gil();
let py = gil.python();
let py_bytes = unsafe {
PyBytes::new_with_uninit(py, 10, |uninit_bytes: &mut [MaybeUninit<u8>]| {
uninit_bytes
.iter_mut()
.zip(b"Hello Rust".into_iter())
.for_each(|(ub, b)| *ub = MaybeUninit::new(*b));
})
};
let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap();
assert_eq!(bytes, b"Hello Rust");
}
}

0 comments on commit 533c105

Please sign in to comment.