@@ -2,53 +2,274 @@ pub(crate) use decl::make_module;
22
33#[ pymodule( name = "marshal" ) ]
44mod decl {
5+ /// TODO add support for Booleans, Sets, etc
6+ use ascii:: AsciiStr ;
7+ use num_bigint:: { BigInt , Sign } ;
8+ use std:: slice:: Iter ;
9+
510 use crate :: {
6- builtins:: { PyBytes , PyCode } ,
11+ builtins:: {
12+ dict:: DictContentType , PyBytes , PyCode , PyDict , PyFloat , PyInt , PyList , PyStr , PyTuple ,
13+ } ,
714 bytecode,
8- function:: ArgBytesLike ,
15+ function:: { ArgBytesLike , IntoPyObject } ,
16+ protocol:: PyBuffer ,
917 PyObjectRef , PyResult , TryFromObject , VirtualMachine ,
1018 } ;
1119
12- #[ pyfunction]
13- fn dumps ( value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyBytes > {
20+ const STR_BYTE : u8 = b's' ;
21+ const INT_BYTE : u8 = b'i' ;
22+ const FLOAT_BYTE : u8 = b'f' ;
23+ const LIST_BYTE : u8 = b'[' ;
24+ const TUPLE_BYTE : u8 = b'(' ;
25+ const DICT_BYTE : u8 = b',' ;
26+
27+ /// Safely convert usize to 4 le bytes
28+ fn size_to_bytes ( x : usize , vm : & VirtualMachine ) -> PyResult < [ u8 ; 4 ] > {
29+ // For marshalling we want to convert lengths to bytes. To save space
30+ // we limit the size to u32 to keep marshalling smaller.
31+ match u32:: try_from ( x) {
32+ Ok ( n) => Ok ( n. to_le_bytes ( ) ) ,
33+ Err ( _) => {
34+ Err ( vm. new_value_error ( "Size exceeds 2^32 capacity for marshaling." . to_owned ( ) ) )
35+ }
36+ }
37+ }
38+
39+ /// Dumps a iterator of objects into binary vector.
40+ fn dump_list ( pyobjs : Iter < PyObjectRef > , vm : & VirtualMachine ) -> PyResult < Vec < u8 > > {
41+ let mut byte_list = size_to_bytes ( pyobjs. len ( ) , vm) ?. to_vec ( ) ;
42+ // For each element, dump into binary, then add its length and value.
43+ for element in pyobjs {
44+ let element_bytes: Vec < u8 > = _dumps ( element. clone ( ) , vm) ?;
45+ byte_list. extend ( size_to_bytes ( element_bytes. len ( ) , vm) ?) ;
46+ byte_list. extend ( element_bytes)
47+ }
48+ Ok ( byte_list)
49+ }
50+
51+ fn _dumps ( value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < Vec < u8 > > {
1452 let r = match_class ! ( match value {
53+ pyint @ PyInt => {
54+ let ( sign, mut int_bytes) = pyint. as_bigint( ) . to_bytes_le( ) ;
55+ let sign_byte = match sign {
56+ Sign :: Minus => b'-' ,
57+ Sign :: NoSign => b'0' ,
58+ Sign :: Plus => b'+' ,
59+ } ;
60+ // Return as [TYPE, SIGN, uint bytes]
61+ int_bytes. insert( 0 , sign_byte) ;
62+ int_bytes. push( INT_BYTE ) ;
63+ int_bytes
64+ }
65+ pyfloat @ PyFloat => {
66+ let mut float_bytes = pyfloat. to_f64( ) . to_le_bytes( ) . to_vec( ) ;
67+ float_bytes. push( FLOAT_BYTE ) ;
68+ float_bytes
69+ }
70+ pystr @ PyStr => {
71+ let mut str_bytes = pystr. as_str( ) . as_bytes( ) . to_vec( ) ;
72+ str_bytes. push( STR_BYTE ) ;
73+ str_bytes
74+ }
75+ pylist @ PyList => {
76+ let pylist_items = pylist. borrow_vec( ) ;
77+ let mut list_bytes = dump_list( pylist_items. iter( ) , vm) ?;
78+ list_bytes. push( LIST_BYTE ) ;
79+ list_bytes
80+ }
81+ pytuple @ PyTuple => {
82+ let mut tuple_bytes = dump_list( pytuple. as_slice( ) . iter( ) , vm) ?;
83+ tuple_bytes. push( TUPLE_BYTE ) ;
84+ tuple_bytes
85+ }
86+ pydict @ PyDict => {
87+ let key_value_pairs = pydict. _as_dict_inner( ) . clone( ) . as_kvpairs( ) ;
88+ // Converts list of tuples to PyObjectRefs of tuples
89+ let elements: Vec <PyObjectRef > = key_value_pairs
90+ . into_iter( )
91+ . map( |( k, v) | PyTuple :: new_ref( vec![ k, v] , & vm. ctx) . into_pyobject( vm) )
92+ . collect( ) ;
93+ // Converts list of tuples to list, dump into binary
94+ let mut dict_bytes = dump_list( elements. iter( ) , vm) ?;
95+ dict_bytes. push( LIST_BYTE ) ;
96+ dict_bytes. push( DICT_BYTE ) ;
97+ dict_bytes
98+ }
1599 co @ PyCode => {
16- PyBytes :: from( co. code. map_clone_bag( & bytecode:: BasicBag ) . to_bytes( ) )
100+ // Code is default, doesn't have prefix.
101+ co. code. map_clone_bag( & bytecode:: BasicBag ) . to_bytes( )
17102 }
18- _ =>
103+ _ => {
19104 return Err ( vm. new_not_implemented_error(
20- "TODO: not implemented yet or marshal unsupported type" . to_owned( )
21- ) ) ,
105+ "TODO: not implemented yet or marshal unsupported type" . to_owned( ) ,
106+ ) ) ;
107+ }
22108 } ) ;
23109 Ok ( r)
24110 }
25111
112+ #[ pyfunction]
113+ fn dumps ( value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyBytes > {
114+ Ok ( PyBytes :: from ( _dumps ( value, vm) ?) )
115+ }
116+
26117 #[ pyfunction]
27118 fn dump ( value : PyObjectRef , f : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
28119 let dumped = dumps ( value, vm) ?;
29120 vm. call_method ( & f, "write" , ( dumped, ) ) ?;
30121 Ok ( ( ) )
31122 }
32123
124+ /// Read the next 4 bytes of a slice, read as u32, pass as usize.
125+ /// Returns the rest of buffer with the value.
126+ fn eat_length < ' a > ( bytes : & ' a [ u8 ] , vm : & VirtualMachine ) -> PyResult < ( usize , & ' a [ u8 ] ) > {
127+ let ( u32_bytes, rest) = bytes. split_at ( 4 ) ;
128+ let length = u32:: from_le_bytes ( u32_bytes. try_into ( ) . map_err ( |_| {
129+ vm. new_value_error ( "Could not read u32 size from byte array" . to_owned ( ) )
130+ } ) ?) ;
131+ Ok ( ( length as usize , rest) )
132+ }
133+
134+ /// Reads next element from a python list. First by getting element size
135+ /// then by building a pybuffer and "loading" the pyobject.
136+ /// Returns rest of buffer with object.
137+ fn next_element_of_list < ' a > (
138+ buf : & ' a [ u8 ] ,
139+ vm : & VirtualMachine ,
140+ ) -> PyResult < ( PyObjectRef , & ' a [ u8 ] ) > {
141+ let ( element_length, element_and_rest) = eat_length ( buf, vm) ?;
142+ let ( element_buff, rest) = element_and_rest. split_at ( element_length) ;
143+ let pybuffer = PyBuffer :: from_byte_vector ( element_buff. to_vec ( ) , vm) ;
144+ Ok ( ( loads ( pybuffer, vm) ?, rest) )
145+ }
146+
147+ /// Reads a list (or tuple) from a buffer.
148+ fn read_list ( buf : & [ u8 ] , vm : & VirtualMachine ) -> PyResult < Vec < PyObjectRef > > {
149+ let ( expected_array_len, mut buffer) = eat_length ( buf, vm) ?;
150+ let mut elements: Vec < PyObjectRef > = Vec :: new ( ) ;
151+ while !buffer. is_empty ( ) {
152+ let ( element, rest_of_buffer) = next_element_of_list ( buffer, vm) ?;
153+ elements. push ( element) ;
154+ buffer = rest_of_buffer;
155+ }
156+ debug_assert ! ( expected_array_len == elements. len( ) ) ;
157+ Ok ( elements)
158+ }
159+
160+ /// Builds a PyDict from iterator of tuple objects
161+ pub fn from_tuples ( iterable : Iter < PyObjectRef > , vm : & VirtualMachine ) -> PyResult < PyDict > {
162+ let dict = DictContentType :: default ( ) ;
163+ for elem in iterable {
164+ let items = match_class ! ( match elem. clone( ) {
165+ pytuple @ PyTuple => pytuple. as_slice( ) . to_vec( ) ,
166+ _ =>
167+ return Err ( vm. new_value_error(
168+ "Couldn't unmarshal key:value pair of dictionary" . to_owned( )
169+ ) ) ,
170+ } ) ;
171+ // Marshalled tuples are always in format key:value.
172+ dict. insert (
173+ vm,
174+ items. get ( 0 ) . unwrap ( ) . clone ( ) ,
175+ items. get ( 1 ) . unwrap ( ) . clone ( ) ,
176+ ) ?;
177+ }
178+ Ok ( PyDict :: from_entries ( dict) )
179+ }
180+
33181 #[ pyfunction]
34- fn loads ( code_bytes : ArgBytesLike , vm : & VirtualMachine ) -> PyResult < PyCode > {
35- let buf = & * code_bytes. borrow_buf ( ) ;
36- let code = bytecode:: CodeObject :: from_bytes ( buf) . map_err ( |e| match e {
37- bytecode:: CodeDeserializeError :: Eof => vm. new_exception_msg (
182+ fn loads ( pybuffer : PyBuffer , vm : & VirtualMachine ) -> PyResult < PyObjectRef > {
183+ let full_buff = pybuffer. as_contiguous ( ) . ok_or_else ( || {
184+ vm. new_buffer_error ( "Buffer provided to marshal.loads() is not contiguous" . to_owned ( ) )
185+ } ) ?;
186+ let ( type_indicator, buf) = full_buff. split_last ( ) . ok_or_else ( || {
187+ vm. new_exception_msg (
38188 vm. ctx . exceptions . eof_error . clone ( ) ,
39- "end of file while deserializing bytecode" . to_owned ( ) ,
40- ) ,
41- _ => vm. new_value_error ( "Couldn't deserialize python bytecode" . to_owned ( ) ) ,
189+ "EOF where object expected." . to_owned ( ) ,
190+ )
42191 } ) ?;
43- Ok ( PyCode {
44- code : vm. map_codeobj ( code) ,
45- } )
192+ match * type_indicator {
193+ INT_BYTE => {
194+ let ( sign_byte, uint_bytes) = buf
195+ . split_first ( )
196+ . ok_or_else ( || vm. new_value_error ( "EOF where object expected." . to_owned ( ) ) ) ?;
197+ let sign = match sign_byte {
198+ b'-' => Sign :: Minus ,
199+ b'0' => Sign :: NoSign ,
200+ b'+' => Sign :: Plus ,
201+ _ => {
202+ return Err ( vm. new_value_error (
203+ "Unknown sign byte when trying to unmarshal integer" . to_owned ( ) ,
204+ ) )
205+ }
206+ } ;
207+ let pyint = BigInt :: from_bytes_le ( sign, uint_bytes) ;
208+ Ok ( pyint. into_pyobject ( vm) )
209+ }
210+ FLOAT_BYTE => {
211+ let number = f64:: from_le_bytes ( match buf[ ..] . try_into ( ) {
212+ Ok ( byte_array) => byte_array,
213+ Err ( e) => {
214+ return Err ( vm. new_value_error ( format ! (
215+ "Expected float, could not load from bytes. {}" ,
216+ e
217+ ) ) )
218+ }
219+ } ) ;
220+ let pyfloat = PyFloat :: from ( number) ;
221+ Ok ( pyfloat. into_pyobject ( vm) )
222+ }
223+ STR_BYTE => {
224+ let pystr = PyStr :: from ( match AsciiStr :: from_ascii ( buf) {
225+ Ok ( ascii_str) => ascii_str,
226+ Err ( e) => {
227+ return Err (
228+ vm. new_value_error ( format ! ( "Cannot unmarshal bytes to string, {}" , e) )
229+ )
230+ }
231+ } ) ;
232+ Ok ( pystr. into_pyobject ( vm) )
233+ }
234+ LIST_BYTE => {
235+ let elements = read_list ( buf, vm) ?;
236+ Ok ( elements. into_pyobject ( vm) )
237+ }
238+ TUPLE_BYTE => {
239+ let elements = read_list ( buf, vm) ?;
240+ let pytuple = PyTuple :: new_ref ( elements, & vm. ctx ) . into_pyobject ( vm) ;
241+ Ok ( pytuple)
242+ }
243+ DICT_BYTE => {
244+ let pybuffer = PyBuffer :: from_byte_vector ( buf[ ..] . to_vec ( ) , vm) ;
245+ let pydict = match_class ! ( match loads( pybuffer, vm) ? {
246+ pylist @ PyList => from_tuples( pylist. borrow_vec( ) . iter( ) , vm) ?,
247+ _ =>
248+ return Err ( vm. new_value_error( "Couldn't unmarshal dicitionary." . to_owned( ) ) ) ,
249+ } ) ;
250+ Ok ( pydict. into_pyobject ( vm) )
251+ }
252+ _ => {
253+ // If prefix is not identifiable, assume CodeObject, error out if it doesn't match.
254+ let code = bytecode:: CodeObject :: from_bytes ( & full_buff) . map_err ( |e| match e {
255+ bytecode:: CodeDeserializeError :: Eof => vm. new_exception_msg (
256+ vm. ctx . exceptions . eof_error . clone ( ) ,
257+ "End of file while deserializing bytecode" . to_owned ( ) ,
258+ ) ,
259+ _ => vm. new_value_error ( "Couldn't deserialize python bytecode" . to_owned ( ) ) ,
260+ } ) ?;
261+ Ok ( PyCode {
262+ code : vm. map_codeobj ( code) ,
263+ }
264+ . into_pyobject ( vm) )
265+ }
266+ }
46267 }
47268
48269 #[ pyfunction]
49- fn load ( f : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyCode > {
270+ fn load ( f : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyObjectRef > {
50271 let read_res = vm. call_method ( & f, "read" , ( ) ) ?;
51272 let bytes = ArgBytesLike :: try_from_object ( vm, read_res) ?;
52- loads ( bytes, vm)
273+ loads ( PyBuffer :: from ( bytes) , vm)
53274 }
54275}
0 commit comments