1 | #![cfg (not(Py_LIMITED_API))] |
2 | |
3 | //! Support for the Python `marshal` format. |
4 | |
5 | use crate::ffi; |
6 | use crate::types::{PyAny, PyBytes}; |
7 | use crate::{AsPyPointer, FromPyPointer, PyResult, Python}; |
8 | use std::os::raw::{c_char, c_int}; |
9 | |
10 | /// The current version of the marshal binary format. |
11 | pub const VERSION: i32 = 4; |
12 | |
13 | /// Serialize an object to bytes using the Python built-in marshal module. |
14 | /// |
15 | /// The built-in marshalling only supports a limited range of objects. |
16 | /// The exact types supported depend on the version argument. |
17 | /// The [`VERSION`] constant holds the highest version currently supported. |
18 | /// |
19 | /// See the [Python documentation](https://docs.python.org/3/library/marshal.html) for more details. |
20 | /// |
21 | /// # Examples |
22 | /// ``` |
23 | /// # use pyo3::{marshal, types::PyDict}; |
24 | /// # pyo3::Python::with_gil(|py| { |
25 | /// let dict = PyDict::new(py); |
26 | /// dict.set_item("aap" , "noot" ).unwrap(); |
27 | /// dict.set_item("mies" , "wim" ).unwrap(); |
28 | /// dict.set_item("zus" , "jet" ).unwrap(); |
29 | /// |
30 | /// let bytes = marshal::dumps(py, dict, marshal::VERSION); |
31 | /// # }); |
32 | /// ``` |
33 | pub fn dumps<'a>(py: Python<'a>, object: &impl AsPyPointer, version: i32) -> PyResult<&'a PyBytes> { |
34 | unsafe { |
35 | let bytes: *mut PyObject = ffi::PyMarshal_WriteObjectToString(object:object.as_ptr(), version as c_int); |
36 | FromPyPointer::from_owned_ptr_or_err(py, ptr:bytes) |
37 | } |
38 | } |
39 | |
40 | /// Deserialize an object from bytes using the Python built-in marshal module. |
41 | pub fn loads<'a, B>(py: Python<'a>, data: &B) -> PyResult<&'a PyAny> |
42 | where |
43 | B: AsRef<[u8]> + ?Sized, |
44 | { |
45 | let data: &[u8] = data.as_ref(); |
46 | unsafe { |
47 | let c_str: *const i8 = data.as_ptr() as *const c_char; |
48 | let object: *mut PyObject = ffi::PyMarshal_ReadObjectFromString(data:c_str, data.len() as isize); |
49 | FromPyPointer::from_owned_ptr_or_err(py, ptr:object) |
50 | } |
51 | } |
52 | |
53 | #[cfg (test)] |
54 | mod tests { |
55 | use super::*; |
56 | use crate::types::PyDict; |
57 | |
58 | #[test ] |
59 | fn marshal_roundtrip() { |
60 | Python::with_gil(|py| { |
61 | let dict = PyDict::new(py); |
62 | dict.set_item("aap" , "noot" ).unwrap(); |
63 | dict.set_item("mies" , "wim" ).unwrap(); |
64 | dict.set_item("zus" , "jet" ).unwrap(); |
65 | |
66 | let bytes = dumps(py, dict, VERSION) |
67 | .expect("marshalling failed" ) |
68 | .as_bytes(); |
69 | let deserialized = loads(py, bytes).expect("unmarshalling failed" ); |
70 | |
71 | assert!(equal(py, dict, deserialized)); |
72 | }); |
73 | } |
74 | |
75 | fn equal(_py: Python<'_>, a: &impl AsPyPointer, b: &impl AsPyPointer) -> bool { |
76 | unsafe { ffi::PyObject_RichCompareBool(a.as_ptr(), b.as_ptr(), ffi::Py_EQ) != 0 } |
77 | } |
78 | } |
79 | |