annotate rust/hg-pyo3/src/util.rs @ 52830:dd3a2948804f

rust-pyo3: add `PyBytesDeref` util This mirrors the one in hg-cpython, explanations inline.
author Rapha?l Gom?s <rgomes@octobus.net>
date Fri, 03 Jan 2025 15:44:29 +0100
parents 4f41a8acf350
children 9749a97d3cfb
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
52822
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
1 use hg::errors::HgError;
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
2 use hg::revlog::inner_revlog::RevisionBuffer;
52780
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
3 use pyo3::buffer::{Element, PyBuffer};
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
4 use pyo3::exceptions::PyValueError;
52407
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
5 use pyo3::prelude::*;
52822
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
6 use pyo3::types::{PyBytes, PyDict};
52830
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
7 use stable_deref_trait::StableDeref;
52822
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
8
52407
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
9 /// Create the module, with `__package__` given from parent
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
10 ///
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
11 /// According to PyO3 documentation, which links to
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
12 /// <https://github.com/PyO3/pyo3/issues/1517>, the same convoluted
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
13 /// write to sys.modules has to be made as with the `cpython` crate.
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
14 pub(crate) fn new_submodule<'py>(
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
15 py: Python<'py>,
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
16 package_name: &str,
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
17 name: &str,
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
18 ) -> PyResult<Bound<'py, PyModule>> {
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
19 let dotted_name = &format!("{}.{}", package_name, name);
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
20 let m = PyModule::new(py, name)?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
21 m.add("__package__", package_name)?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
22 m.add("__doc__", "DAG operations - Rust implementation")?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
23
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
24 let sys = PyModule::import(py, "sys")?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
25 // according to the doc, we could make a static PyString out of
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
26 // "modules" with the `intern!` macro, but this is used only at
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
27 // registration so it may not be worth the effort.
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
28 let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.extract()?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
29 sys_modules.set_item(dotted_name, &m)?;
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
30 // Example C code (see pyexpat.c and import.c) will "give away the
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
31 // reference", but we won't because it will be consumed once the
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
32 // Rust PyObject is dropped.
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
33 Ok(m)
c5128c541021 rust-pyo3: facility for submodule registration, using it for dagop
Georges Racinet <georges.racinet@cloudcrane.io>
parents:
diff changeset
34 }
52780
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
35
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
36 /// Type shortcut for the kind of bytes slice trait objects that are used in
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
37 /// particular for mmap data
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
38 type BoxedBytesSlice =
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
39 Box<dyn std::ops::Deref<Target = [u8]> + Send + Sync + 'static>;
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
40
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
41 /// Take a Python object backed by a Python buffer, and return the underlying
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
42 /// [`PyBuffer`] along with the Rust slice into said buffer.
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
43 ///
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
44 /// The caller needs to make sure that the Python buffer is not freed before
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
45 /// the slice, otherwise we'd get a dangling pointer once the incoming
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
46 /// object is freed from Python side. This can be achieved by storing it a
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
47 /// Python object.
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
48 ///
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
49 /// The typical use case is to extract mmap data to make it useable in the
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
50 /// constructs from the `hg` crate.
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
51 ///
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
52 /// # Safety
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
53 ///
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
54 /// The caller must make sure that the incoming Python object is kept around
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
55 /// for at least as long as the returned [`BoxedBytesSlice`].
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
56 // TODO in PyO3, we already get a reference with two lifetimes, and we
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
57 // could even take a `Borrowed<'a, 'py, T>`.
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
58 // So perhaps we could tie everything together with a lifetime so that is
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
59 // is, after all, safe, and this could be called something like `share_buffer`.
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
60 #[deny(unsafe_op_in_unsafe_fn)]
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
61 pub unsafe fn take_buffer_with_slice(
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
62 data: &Bound<'_, PyAny>,
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
63 ) -> PyResult<(PyBuffer<u8>, BoxedBytesSlice)> {
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
64 let buf = PyBuffer::<u8>::get(data)?;
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
65 let len = buf.item_count();
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
66
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
67 // Build a slice from the buffer data
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
68 let cbuf = buf.buf_ptr();
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
69 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
70 && buf.is_c_contiguous()
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
71 && u8::is_compatible_format(buf.format())
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
72 && buf.dimensions() == 1
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
73 && buf.readonly()
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
74 {
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
75 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
76 } else {
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
77 return Err(PyValueError::new_err(
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
78 "buffer has an invalid memory representation",
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
79 ));
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
80 };
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
81
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
82 Ok((buf, Box::new(bytes)))
42b219a1404a rust-pyo3-revlog: InnerRevlog definition and constructor
Georges Racinet <georges.racinet@cloudcrane.io>
parents: 52407
diff changeset
83 }
52822
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
84
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
85 /// Takes an initialization function `init` which writes bytes to a
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
86 /// Python-backed buffer, to save on a (potentially large) memory allocation
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
87 /// and copy. If `init` fails to write the full expected length `len`, an error
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
88 /// is raised.
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
89 pub fn with_pybytes_buffer<F>(
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
90 py: Python,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
91 len: usize,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
92 init: F,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
93 ) -> Result<Py<PyBytes>, hg::revlog::RevlogError>
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
94 where
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
95 F: FnOnce(
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
96 &mut dyn RevisionBuffer<Target = Py<PyBytes>>,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
97 ) -> Result<(), hg::revlog::RevlogError>,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
98 {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
99 // Largely inspired by code in PyO3
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
100 // https://pyo3.rs/main/doc/pyo3/types/struct.pybytes#method.new_bound_with
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
101 unsafe {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
102 let pyptr = pyo3::ffi::PyBytes_FromStringAndSize(
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
103 std::ptr::null(),
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
104 len as pyo3::ffi::Py_ssize_t,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
105 );
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
106 let pybytes = Bound::from_owned_ptr_or_err(py, pyptr)
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
107 .map_err(|e| HgError::abort_simple(e.to_string()))?
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
108 .downcast_into_unchecked();
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
109 let buffer: *mut u8 = pyo3::ffi::PyBytes_AsString(pyptr).cast();
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
110 debug_assert!(!buffer.is_null());
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
111 let mut rev_buf = PyRevisionBuffer::new(pybytes.unbind(), buffer, len);
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
112 // Initialise the bytestring in init
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
113 // If init returns an Err, the buffer is deallocated by `pybytes`
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
114 init(&mut rev_buf).map(|_| rev_buf.finish())
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
115 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
116 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
117
52830
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
118 /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
119 /// that borrows it. Implements `Deref<Target = [u8]>`.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
120 ///
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
121 /// Calling `PyBytes::data` requires a GIL marker but we want to access the
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
122 /// data in a thread that (ideally) does not need to acquire the GIL.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
123 /// This type allows separating the call an the use.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
124 ///
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
125 /// It also enables using a (wrapped) `PyBytes` in GIL-unaware generic code.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
126 pub struct PyBytesDeref {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
127 #[allow(unused)]
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
128 keep_alive: Py<PyBytes>,
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
129
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
130 /// Borrows the buffer inside `self.keep_alive`,
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
131 /// but the borrow-checker cannot express self-referential structs.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
132 data: *const [u8],
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
133 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
134
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
135 impl PyBytesDeref {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
136 pub fn new(py: Python, bytes: Py<PyBytes>) -> Self {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
137 Self {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
138 data: bytes.as_bytes(py),
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
139 keep_alive: bytes,
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
140 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
141 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
142
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
143 #[allow(unused)]
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
144 pub fn unwrap(self) -> Py<PyBytes> {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
145 self.keep_alive
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
146 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
147 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
148
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
149 impl std::ops::Deref for PyBytesDeref {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
150 type Target = [u8];
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
151
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
152 fn deref(&self) -> &[u8] {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
153 // Safety: the raw pointer is valid as long as the PyBytes is still
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
154 // alive, and the returned slice borrows `self`.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
155 unsafe { &*self.data }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
156 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
157 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
158
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
159 unsafe impl StableDeref for PyBytesDeref {}
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
160
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
161 fn require_send<T: Send>() {}
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
162
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
163 #[allow(unused)]
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
164 fn static_assert_pybytes_is_send() {
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
165 #[allow(clippy::no_effect)]
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
166 require_send::<Py<PyBytes>>;
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
167 }
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
168
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
169 // Safety: `[Py<PyBytes>]` is Send. Raw pointers are not by default,
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
170 // but here sending one to another thread is fine since we ensure it stays
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
171 // valid.
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
172 unsafe impl Send for PyBytesDeref {}
dd3a2948804f rust-pyo3: add `PyBytesDeref` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52822
diff changeset
173
52822
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
174 /// Wrapper around a Python-provided buffer into which the revision contents
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
175 /// will be written. Done for speed in order to save a large allocation + copy.
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
176 struct PyRevisionBuffer {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
177 py_bytes: Py<PyBytes>,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
178 _buf: *mut u8,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
179 len: usize,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
180 current_buf: *mut u8,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
181 current_len: usize,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
182 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
183
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
184 impl PyRevisionBuffer {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
185 /// # Safety
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
186 ///
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
187 /// `buf` should be the start of the allocated bytes of `bytes`, and `len`
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
188 /// exactly the length of said allocated bytes.
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
189 #[inline]
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
190 unsafe fn new(bytes: Py<PyBytes>, buf: *mut u8, len: usize) -> Self {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
191 Self {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
192 py_bytes: bytes,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
193 _buf: buf,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
194 len,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
195 current_len: 0,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
196 current_buf: buf,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
197 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
198 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
199
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
200 /// Number of bytes that have been copied to. Will be different to the
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
201 /// total allocated length of the buffer unless the revision is done being
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
202 /// written.
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
203 #[inline]
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
204 fn current_len(&self) -> usize {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
205 self.current_len
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
206 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
207 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
208
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
209 impl RevisionBuffer for PyRevisionBuffer {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
210 type Target = Py<PyBytes>;
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
211
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
212 #[inline]
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
213 fn extend_from_slice(&mut self, slice: &[u8]) {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
214 assert!(self.current_len + slice.len() <= self.len);
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
215 unsafe {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
216 // We cannot use `copy_from_nonoverlapping` since it's *possible*
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
217 // to create a slice from the same Python memory region using
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
218 // [`PyBytesDeref`]. Probable that LLVM has an optimization anyway?
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
219 self.current_buf.copy_from(slice.as_ptr(), slice.len());
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
220 self.current_buf = self.current_buf.add(slice.len());
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
221 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
222 self.current_len += slice.len()
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
223 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
224
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
225 #[inline]
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
226 fn finish(self) -> Self::Target {
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
227 // catch unzeroed bytes before it becomes undefined behavior
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
228 assert_eq!(
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
229 self.current_len(),
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
230 self.len,
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
231 "not enough bytes read for revision"
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
232 );
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
233 self.py_bytes
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
234 }
4f41a8acf350 rust-pyo3: add a `with_pybytes_buffer` util
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52780
diff changeset
235 }