11//! Contains types for working with Python objects that own the underlying data.
22
3- use std:: { ops:: Deref , ptr:: NonNull } ;
3+ use std:: { ops:: Deref , ptr:: NonNull , sync :: Arc } ;
44
55use crate :: {
66 types:: {
@@ -13,6 +13,7 @@ use crate::{
1313/// A wrapper around `str` where the storage is owned by a Python `bytes` or `str` object.
1414///
1515/// This type gives access to the underlying data via a `Deref` implementation.
16+ #[ derive( Clone ) ]
1617pub struct PyBackedStr {
1718 #[ allow( dead_code) ] // only held so that the storage is not dropped
1819 storage : Py < PyAny > ,
@@ -44,6 +45,14 @@ impl AsRef<[u8]> for PyBackedStr {
4445unsafe impl Send for PyBackedStr { }
4546unsafe impl Sync for PyBackedStr { }
4647
48+ impl std:: fmt:: Display for PyBackedStr {
49+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
50+ self . deref ( ) . fmt ( f)
51+ }
52+ }
53+
54+ impl_traits ! ( PyBackedStr , str ) ;
55+
4756impl TryFrom < Bound < ' _ , PyString > > for PyBackedStr {
4857 type Error = PyErr ;
4958 fn try_from ( py_string : Bound < ' _ , PyString > ) -> Result < Self , Self :: Error > {
@@ -79,16 +88,18 @@ impl FromPyObject<'_> for PyBackedStr {
7988/// A wrapper around `[u8]` where the storage is either owned by a Python `bytes` object, or a Rust `Box<[u8]>`.
8089///
8190/// This type gives access to the underlying data via a `Deref` implementation.
91+ #[ derive( Clone ) ]
8292pub struct PyBackedBytes {
8393 #[ allow( dead_code) ] // only held so that the storage is not dropped
8494 storage : PyBackedBytesStorage ,
8595 data : NonNull < [ u8 ] > ,
8696}
8797
8898#[ allow( dead_code) ]
99+ #[ derive( Clone ) ]
89100enum PyBackedBytesStorage {
90101 Python ( Py < PyBytes > ) ,
91- Rust ( Box < [ u8 ] > ) ,
102+ Rust ( Arc < [ u8 ] > ) ,
92103}
93104
94105impl Deref for PyBackedBytes {
@@ -110,6 +121,32 @@ impl AsRef<[u8]> for PyBackedBytes {
110121unsafe impl Send for PyBackedBytes { }
111122unsafe impl Sync for PyBackedBytes { }
112123
124+ impl < const N : usize > PartialEq < [ u8 ; N ] > for PyBackedBytes {
125+ fn eq ( & self , other : & [ u8 ; N ] ) -> bool {
126+ self . deref ( ) == other
127+ }
128+ }
129+
130+ impl < const N : usize > PartialEq < PyBackedBytes > for [ u8 ; N ] {
131+ fn eq ( & self , other : & PyBackedBytes ) -> bool {
132+ self == other. deref ( )
133+ }
134+ }
135+
136+ impl < const N : usize > PartialEq < & [ u8 ; N ] > for PyBackedBytes {
137+ fn eq ( & self , other : & & [ u8 ; N ] ) -> bool {
138+ self . deref ( ) == * other
139+ }
140+ }
141+
142+ impl < const N : usize > PartialEq < PyBackedBytes > for & [ u8 ; N ] {
143+ fn eq ( & self , other : & PyBackedBytes ) -> bool {
144+ self == & other. deref ( )
145+ }
146+ }
147+
148+ impl_traits ! ( PyBackedBytes , [ u8 ] ) ;
149+
113150impl From < Bound < ' _ , PyBytes > > for PyBackedBytes {
114151 fn from ( py_bytes : Bound < ' _ , PyBytes > ) -> Self {
115152 let b = py_bytes. as_bytes ( ) ;
@@ -123,7 +160,7 @@ impl From<Bound<'_, PyBytes>> for PyBackedBytes {
123160
124161impl From < Bound < ' _ , PyByteArray > > for PyBackedBytes {
125162 fn from ( py_bytearray : Bound < ' _ , PyByteArray > ) -> Self {
126- let s = py_bytearray. to_vec ( ) . into_boxed_slice ( ) ;
163+ let s = Arc :: < [ u8 ] > :: from ( py_bytearray. to_vec ( ) ) ;
127164 let data = NonNull :: from ( s. as_ref ( ) ) ;
128165 Self {
129166 storage : PyBackedBytesStorage :: Rust ( s) ,
@@ -144,10 +181,85 @@ impl FromPyObject<'_> for PyBackedBytes {
144181 }
145182}
146183
184+ macro_rules! impl_traits {
185+ ( $slf: ty, $equiv: ty) => {
186+ impl std:: fmt:: Debug for $slf {
187+ fn fmt( & self , f: & mut std:: fmt:: Formatter <' _>) -> std:: fmt:: Result {
188+ self . deref( ) . fmt( f)
189+ }
190+ }
191+
192+ impl PartialEq for $slf {
193+ fn eq( & self , other: & Self ) -> bool {
194+ self . deref( ) == other. deref( )
195+ }
196+ }
197+
198+ impl PartialEq <$equiv> for $slf {
199+ fn eq( & self , other: & $equiv) -> bool {
200+ self . deref( ) == other
201+ }
202+ }
203+
204+ impl PartialEq <& $equiv> for $slf {
205+ fn eq( & self , other: &&$equiv) -> bool {
206+ self . deref( ) == * other
207+ }
208+ }
209+
210+ impl PartialEq <$slf> for $equiv {
211+ fn eq( & self , other: & $slf) -> bool {
212+ self == other. deref( )
213+ }
214+ }
215+
216+ impl PartialEq <$slf> for & $equiv {
217+ fn eq( & self , other: & $slf) -> bool {
218+ self == & other. deref( )
219+ }
220+ }
221+
222+ impl Eq for $slf { }
223+
224+ impl PartialOrd for $slf {
225+ fn partial_cmp( & self , other: & Self ) -> Option <std:: cmp:: Ordering > {
226+ Some ( self . cmp( other) )
227+ }
228+ }
229+
230+ impl PartialOrd <$equiv> for $slf {
231+ fn partial_cmp( & self , other: & $equiv) -> Option <std:: cmp:: Ordering > {
232+ self . deref( ) . partial_cmp( other)
233+ }
234+ }
235+
236+ impl PartialOrd <$slf> for $equiv {
237+ fn partial_cmp( & self , other: & $slf) -> Option <std:: cmp:: Ordering > {
238+ self . partial_cmp( other. deref( ) )
239+ }
240+ }
241+
242+ impl Ord for $slf {
243+ fn cmp( & self , other: & Self ) -> std:: cmp:: Ordering {
244+ self . deref( ) . cmp( other. deref( ) )
245+ }
246+ }
247+
248+ impl std:: hash:: Hash for $slf {
249+ fn hash<H : std:: hash:: Hasher >( & self , state: & mut H ) {
250+ self . deref( ) . hash( state)
251+ }
252+ }
253+ } ;
254+ }
255+ use impl_traits;
256+
147257#[ cfg( test) ]
148258mod test {
149259 use super :: * ;
150260 use crate :: Python ;
261+ use std:: collections:: hash_map:: DefaultHasher ;
262+ use std:: hash:: { Hash , Hasher } ;
151263
152264 #[ test]
153265 fn py_backed_str_empty ( ) {
@@ -223,4 +335,149 @@ mod test {
223335 is_send :: < PyBackedBytes > ( ) ;
224336 is_sync :: < PyBackedBytes > ( ) ;
225337 }
338+
339+ #[ test]
340+ fn test_backed_str_clone ( ) {
341+ Python :: with_gil ( |py| {
342+ let s1: PyBackedStr = PyString :: new_bound ( py, "hello" ) . try_into ( ) . unwrap ( ) ;
343+ let s2 = s1. clone ( ) ;
344+ assert_eq ! ( s1, s2) ;
345+
346+ drop ( s1) ;
347+ assert_eq ! ( s2, "hello" ) ;
348+ } ) ;
349+ }
350+
351+ #[ test]
352+ fn test_backed_str_eq ( ) {
353+ Python :: with_gil ( |py| {
354+ let s1: PyBackedStr = PyString :: new_bound ( py, "hello" ) . try_into ( ) . unwrap ( ) ;
355+ let s2: PyBackedStr = PyString :: new_bound ( py, "hello" ) . try_into ( ) . unwrap ( ) ;
356+ assert_eq ! ( s1, "hello" ) ;
357+ assert_eq ! ( s1, s2) ;
358+
359+ let s3: PyBackedStr = PyString :: new_bound ( py, "abcde" ) . try_into ( ) . unwrap ( ) ;
360+ assert_eq ! ( "abcde" , s3) ;
361+ assert_ne ! ( s1, s3) ;
362+ } ) ;
363+ }
364+
365+ #[ test]
366+ fn test_backed_str_hash ( ) {
367+ Python :: with_gil ( |py| {
368+ let h = {
369+ let mut hasher = DefaultHasher :: new ( ) ;
370+ "abcde" . hash ( & mut hasher) ;
371+ hasher. finish ( )
372+ } ;
373+
374+ let s1: PyBackedStr = PyString :: new_bound ( py, "abcde" ) . try_into ( ) . unwrap ( ) ;
375+ let h1 = {
376+ let mut hasher = DefaultHasher :: new ( ) ;
377+ s1. hash ( & mut hasher) ;
378+ hasher. finish ( )
379+ } ;
380+
381+ assert_eq ! ( h, h1) ;
382+ } ) ;
383+ }
384+
385+ #[ test]
386+ fn test_backed_str_ord ( ) {
387+ Python :: with_gil ( |py| {
388+ let mut a = vec ! [ "a" , "c" , "d" , "b" , "f" , "g" , "e" ] ;
389+ let mut b = a
390+ . iter ( )
391+ . map ( |s| PyString :: new_bound ( py, s) . try_into ( ) . unwrap ( ) )
392+ . collect :: < Vec < PyBackedStr > > ( ) ;
393+
394+ a. sort ( ) ;
395+ b. sort ( ) ;
396+
397+ assert_eq ! ( a, b) ;
398+ } )
399+ }
400+
401+ #[ test]
402+ fn test_backed_bytes_from_bytes_clone ( ) {
403+ Python :: with_gil ( |py| {
404+ let b1: PyBackedBytes = PyBytes :: new_bound ( py, b"abcde" ) . into ( ) ;
405+ let b2 = b1. clone ( ) ;
406+ assert_eq ! ( b1, b2) ;
407+
408+ drop ( b1) ;
409+ assert_eq ! ( b2, b"abcde" ) ;
410+ } ) ;
411+ }
412+
413+ #[ test]
414+ fn test_backed_bytes_from_bytearray_clone ( ) {
415+ Python :: with_gil ( |py| {
416+ let b1: PyBackedBytes = PyByteArray :: new_bound ( py, b"abcde" ) . into ( ) ;
417+ let b2 = b1. clone ( ) ;
418+ assert_eq ! ( b1, b2) ;
419+
420+ drop ( b1) ;
421+ assert_eq ! ( b2, b"abcde" ) ;
422+ } ) ;
423+ }
424+
425+ #[ test]
426+ fn test_backed_bytes_eq ( ) {
427+ Python :: with_gil ( |py| {
428+ let b1: PyBackedBytes = PyBytes :: new_bound ( py, b"abcde" ) . into ( ) ;
429+ let b2: PyBackedBytes = PyByteArray :: new_bound ( py, b"abcde" ) . into ( ) ;
430+
431+ assert_eq ! ( b1, b"abcde" ) ;
432+ assert_eq ! ( b1, b2) ;
433+
434+ let b3: PyBackedBytes = PyBytes :: new_bound ( py, b"hello" ) . into ( ) ;
435+ assert_eq ! ( b"hello" , b3) ;
436+ assert_ne ! ( b1, b3) ;
437+ } ) ;
438+ }
439+
440+ #[ test]
441+ fn test_backed_bytes_hash ( ) {
442+ Python :: with_gil ( |py| {
443+ let h = {
444+ let mut hasher = DefaultHasher :: new ( ) ;
445+ b"abcde" . hash ( & mut hasher) ;
446+ hasher. finish ( )
447+ } ;
448+
449+ let b1: PyBackedBytes = PyBytes :: new_bound ( py, b"abcde" ) . into ( ) ;
450+ let h1 = {
451+ let mut hasher = DefaultHasher :: new ( ) ;
452+ b1. hash ( & mut hasher) ;
453+ hasher. finish ( )
454+ } ;
455+
456+ let b2: PyBackedBytes = PyByteArray :: new_bound ( py, b"abcde" ) . into ( ) ;
457+ let h2 = {
458+ let mut hasher = DefaultHasher :: new ( ) ;
459+ b2. hash ( & mut hasher) ;
460+ hasher. finish ( )
461+ } ;
462+
463+ assert_eq ! ( h, h1) ;
464+ assert_eq ! ( h, h2) ;
465+ } ) ;
466+ }
467+
468+ #[ test]
469+ fn test_backed_bytes_ord ( ) {
470+ Python :: with_gil ( |py| {
471+ let mut a = vec ! [ b"a" , b"c" , b"d" , b"b" , b"f" , b"g" , b"e" ] ;
472+ let mut b = a
473+ . iter ( )
474+ . map ( |& b| PyBytes :: new_bound ( py, b) . into ( ) )
475+ . collect :: < Vec < PyBackedBytes > > ( ) ;
476+
477+ a. sort ( ) ;
478+ b. sort ( ) ;
479+
480+ assert_eq ! ( a, b) ;
481+ } )
482+ }
226483}
0 commit comments