-
Notifications
You must be signed in to change notification settings - Fork 784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Rust initialisation of Python-allocated bytes #1074
Changes from 2 commits
98b9f1c
20d27ff
533c105
35d2ef1
f4a5a22
0a47af7
cac2d8a
cb07c67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,60 @@ impl PyBytes { | |
unsafe { py.from_owned_ptr(ffi::PyBytes_FromStringAndSize(ptr, len)) } | ||
} | ||
|
||
/// Creates a new Python bytestring object and a mutable slice to its underlying buffer. | ||
/// 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) { | ||
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; | ||
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`. | ||
/// | ||
/// Panics if out of memory. | ||
pub fn new_with<F: Fn(&mut [u8])>(py: Python<'_>, len: usize, init: F) -> &PyBytes { | ||
juntyr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is optimized by the compiler and not so slow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 533c105 |
||
} | ||
init(slice); | ||
pybytes | ||
} | ||
|
||
/// Creates a new Python bytestring object. | ||
/// | ||
/// # Safety | ||
/// * The bytestring is uninitialised and must not be read inside `init`. | ||
/// * The entire bytestring must be written to inside `init` such that any code that | ||
/// follows does not read uninitialised memory. | ||
/// | ||
/// Panics if out of memory. | ||
pub unsafe fn new_with_uninit<F: Fn(&mut [u8])>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If so, the closure should take There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed in 533c105 |
||
py: Python<'_>, | ||
len: usize, | ||
init: F, | ||
) -> &PyBytes { | ||
let (slice, pybytes) = Self::new_with_uninit_impl(py, len); | ||
init(slice); | ||
pybytes | ||
} | ||
|
||
/// Creates a new Python byte string object from a raw pointer and length. | ||
/// | ||
/// Panics if out of memory. | ||
|
@@ -91,4 +145,24 @@ mod test { | |
let bytes = PyBytes::new(py, b"Hello World"); | ||
assert_eq!(bytes[1], b'e'); | ||
} | ||
|
||
#[test] | ||
fn test_bytes_new_with() { | ||
let gil = Python::acquire_gil(); | ||
let py = gil.python(); | ||
let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| { | ||
b.copy_from_slice(b"Hello Rust"); | ||
}); | ||
let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap(); | ||
assert_eq!(bytes, b"Hello Rust"); | ||
} | ||
|
||
#[test] | ||
fn test_bytes_new_with_zero_initialised() { | ||
let gil = Python::acquire_gil(); | ||
let py = gil.python(); | ||
let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| ()); | ||
let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap(); | ||
assert_eq!(bytes, &[0; 10]); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need
#Safety
document for safe functionsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 533c105