1use std::{convert::Infallible, ops::Deref, ptr::NonNull, sync::Arc};
4
5use crate::{
6 types::{
7 any::PyAnyMethods, bytearray::PyByteArrayMethods, bytes::PyBytesMethods,
8 string::PyStringMethods, PyByteArray, PyBytes, PyString,
9 },
10 Bound, DowncastError, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python,
11};
12#[allow(deprecated)]
13use crate::{IntoPy, ToPyObject};
14
15#[cfg_attr(feature = "py-clone", derive(Clone))]
19pub struct PyBackedStr {
20 #[allow(dead_code)] storage: Py<PyAny>,
22 data: NonNull<str>,
23}
24
25impl Deref for PyBackedStr {
26 type Target = str;
27 fn deref(&self) -> &str {
28 unsafe { self.data.as_ref() }
30 }
31}
32
33impl AsRef<str> for PyBackedStr {
34 fn as_ref(&self) -> &str {
35 self
36 }
37}
38
39impl AsRef<[u8]> for PyBackedStr {
40 fn as_ref(&self) -> &[u8] {
41 self.as_bytes()
42 }
43}
44
45unsafe impl Send for PyBackedStr {}
48unsafe impl Sync for PyBackedStr {}
49
50impl std::fmt::Display for PyBackedStr {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 self.deref().fmt(f)
53 }
54}
55
56impl_traits!(PyBackedStr, str);
57
58impl TryFrom<Bound<'_, PyString>> for PyBackedStr {
59 type Error = PyErr;
60 fn try_from(py_string: Bound<'_, PyString>) -> Result<Self, Self::Error> {
61 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
62 {
63 let s = py_string.to_str()?;
64 let data = NonNull::from(s);
65 Ok(Self {
66 storage: py_string.into_any().unbind(),
67 data,
68 })
69 }
70 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
71 {
72 let bytes = py_string.encode_utf8()?;
73 let s = unsafe { std::str::from_utf8_unchecked(bytes.as_bytes()) };
74 let data = NonNull::from(s);
75 Ok(Self {
76 storage: bytes.into_any().unbind(),
77 data,
78 })
79 }
80 }
81}
82
83impl FromPyObject<'_> for PyBackedStr {
84 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
85 let py_string = obj.downcast::<PyString>()?.to_owned();
86 Self::try_from(py_string)
87 }
88}
89
90#[allow(deprecated)]
91impl ToPyObject for PyBackedStr {
92 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
93 fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
94 self.storage.as_any().clone_ref(py)
95 }
96 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
97 fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
98 PyString::new(py, self).into_any().unbind()
99 }
100}
101
102#[allow(deprecated)]
103impl IntoPy<Py<PyAny>> for PyBackedStr {
104 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
105 fn into_py(self, _py: Python<'_>) -> Py<PyAny> {
106 self.storage.into_any()
107 }
108 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
109 fn into_py(self, py: Python<'_>) -> Py<PyAny> {
110 PyString::new(py, &self).into_any().unbind()
111 }
112}
113
114impl<'py> IntoPyObject<'py> for PyBackedStr {
115 type Target = PyAny;
116 type Output = Bound<'py, Self::Target>;
117 type Error = Infallible;
118
119 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
120 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
121 Ok(self.storage.into_bound(py))
122 }
123
124 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
125 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
126 Ok(PyString::new(py, &self).into_any())
127 }
128}
129
130impl<'py> IntoPyObject<'py> for &PyBackedStr {
131 type Target = PyAny;
132 type Output = Bound<'py, Self::Target>;
133 type Error = Infallible;
134
135 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
136 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
137 Ok(self.storage.bind(py).to_owned())
138 }
139
140 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
141 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
142 Ok(PyString::new(py, self).into_any())
143 }
144}
145
146#[cfg_attr(feature = "py-clone", derive(Clone))]
150pub struct PyBackedBytes {
151 #[allow(dead_code)] storage: PyBackedBytesStorage,
153 data: NonNull<[u8]>,
154}
155
156#[allow(dead_code)]
157#[cfg_attr(feature = "py-clone", derive(Clone))]
158enum PyBackedBytesStorage {
159 Python(Py<PyBytes>),
160 Rust(Arc<[u8]>),
161}
162
163impl Deref for PyBackedBytes {
164 type Target = [u8];
165 fn deref(&self) -> &[u8] {
166 unsafe { self.data.as_ref() }
168 }
169}
170
171impl AsRef<[u8]> for PyBackedBytes {
172 fn as_ref(&self) -> &[u8] {
173 self
174 }
175}
176
177unsafe impl Send for PyBackedBytes {}
180unsafe impl Sync for PyBackedBytes {}
181
182impl<const N: usize> PartialEq<[u8; N]> for PyBackedBytes {
183 fn eq(&self, other: &[u8; N]) -> bool {
184 self.deref() == other
185 }
186}
187
188impl<const N: usize> PartialEq<PyBackedBytes> for [u8; N] {
189 fn eq(&self, other: &PyBackedBytes) -> bool {
190 self == other.deref()
191 }
192}
193
194impl<const N: usize> PartialEq<&[u8; N]> for PyBackedBytes {
195 fn eq(&self, other: &&[u8; N]) -> bool {
196 self.deref() == *other
197 }
198}
199
200impl<const N: usize> PartialEq<PyBackedBytes> for &[u8; N] {
201 fn eq(&self, other: &PyBackedBytes) -> bool {
202 self == &other.deref()
203 }
204}
205
206impl_traits!(PyBackedBytes, [u8]);
207
208impl From<Bound<'_, PyBytes>> for PyBackedBytes {
209 fn from(py_bytes: Bound<'_, PyBytes>) -> Self {
210 let b = py_bytes.as_bytes();
211 let data = NonNull::from(b);
212 Self {
213 storage: PyBackedBytesStorage::Python(py_bytes.to_owned().unbind()),
214 data,
215 }
216 }
217}
218
219impl From<Bound<'_, PyByteArray>> for PyBackedBytes {
220 fn from(py_bytearray: Bound<'_, PyByteArray>) -> Self {
221 let s = Arc::<[u8]>::from(py_bytearray.to_vec());
222 let data = NonNull::from(s.as_ref());
223 Self {
224 storage: PyBackedBytesStorage::Rust(s),
225 data,
226 }
227 }
228}
229
230impl FromPyObject<'_> for PyBackedBytes {
231 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
232 if let Ok(bytes) = obj.downcast::<PyBytes>() {
233 Ok(Self::from(bytes.to_owned()))
234 } else if let Ok(bytearray) = obj.downcast::<PyByteArray>() {
235 Ok(Self::from(bytearray.to_owned()))
236 } else {
237 Err(DowncastError::new(obj, "`bytes` or `bytearray`").into())
238 }
239 }
240}
241
242#[allow(deprecated)]
243impl ToPyObject for PyBackedBytes {
244 fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
245 match &self.storage {
246 PyBackedBytesStorage::Python(bytes) => bytes.to_object(py),
247 PyBackedBytesStorage::Rust(bytes) => PyBytes::new(py, bytes).into_any().unbind(),
248 }
249 }
250}
251
252#[allow(deprecated)]
253impl IntoPy<Py<PyAny>> for PyBackedBytes {
254 fn into_py(self, py: Python<'_>) -> Py<PyAny> {
255 match self.storage {
256 PyBackedBytesStorage::Python(bytes) => bytes.into_any(),
257 PyBackedBytesStorage::Rust(bytes) => PyBytes::new(py, &bytes).into_any().unbind(),
258 }
259 }
260}
261
262impl<'py> IntoPyObject<'py> for PyBackedBytes {
263 type Target = PyBytes;
264 type Output = Bound<'py, Self::Target>;
265 type Error = Infallible;
266
267 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
268 match self.storage {
269 PyBackedBytesStorage::Python(bytes) => Ok(bytes.into_bound(py)),
270 PyBackedBytesStorage::Rust(bytes) => Ok(PyBytes::new(py, &bytes)),
271 }
272 }
273}
274
275impl<'py> IntoPyObject<'py> for &PyBackedBytes {
276 type Target = PyBytes;
277 type Output = Bound<'py, Self::Target>;
278 type Error = Infallible;
279
280 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
281 match &self.storage {
282 PyBackedBytesStorage::Python(bytes) => Ok(bytes.bind(py).clone()),
283 PyBackedBytesStorage::Rust(bytes) => Ok(PyBytes::new(py, bytes)),
284 }
285 }
286}
287
288macro_rules! impl_traits {
289 ($slf:ty, $equiv:ty) => {
290 impl std::fmt::Debug for $slf {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 self.deref().fmt(f)
293 }
294 }
295
296 impl PartialEq for $slf {
297 fn eq(&self, other: &Self) -> bool {
298 self.deref() == other.deref()
299 }
300 }
301
302 impl PartialEq<$equiv> for $slf {
303 fn eq(&self, other: &$equiv) -> bool {
304 self.deref() == other
305 }
306 }
307
308 impl PartialEq<&$equiv> for $slf {
309 fn eq(&self, other: &&$equiv) -> bool {
310 self.deref() == *other
311 }
312 }
313
314 impl PartialEq<$slf> for $equiv {
315 fn eq(&self, other: &$slf) -> bool {
316 self == other.deref()
317 }
318 }
319
320 impl PartialEq<$slf> for &$equiv {
321 fn eq(&self, other: &$slf) -> bool {
322 self == &other.deref()
323 }
324 }
325
326 impl Eq for $slf {}
327
328 impl PartialOrd for $slf {
329 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
330 Some(self.cmp(other))
331 }
332 }
333
334 impl PartialOrd<$equiv> for $slf {
335 fn partial_cmp(&self, other: &$equiv) -> Option<std::cmp::Ordering> {
336 self.deref().partial_cmp(other)
337 }
338 }
339
340 impl PartialOrd<$slf> for $equiv {
341 fn partial_cmp(&self, other: &$slf) -> Option<std::cmp::Ordering> {
342 self.partial_cmp(other.deref())
343 }
344 }
345
346 impl Ord for $slf {
347 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
348 self.deref().cmp(other.deref())
349 }
350 }
351
352 impl std::hash::Hash for $slf {
353 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
354 self.deref().hash(state)
355 }
356 }
357 };
358}
359use impl_traits;
360
361#[cfg(test)]
362mod test {
363 use super::*;
364 use crate::{IntoPyObject, Python};
365 use std::collections::hash_map::DefaultHasher;
366 use std::hash::{Hash, Hasher};
367
368 #[test]
369 fn py_backed_str_empty() {
370 Python::with_gil(|py| {
371 let s = PyString::new(py, "");
372 let py_backed_str = s.extract::<PyBackedStr>().unwrap();
373 assert_eq!(&*py_backed_str, "");
374 });
375 }
376
377 #[test]
378 fn py_backed_str() {
379 Python::with_gil(|py| {
380 let s = PyString::new(py, "hello");
381 let py_backed_str = s.extract::<PyBackedStr>().unwrap();
382 assert_eq!(&*py_backed_str, "hello");
383 });
384 }
385
386 #[test]
387 fn py_backed_str_try_from() {
388 Python::with_gil(|py| {
389 let s = PyString::new(py, "hello");
390 let py_backed_str = PyBackedStr::try_from(s).unwrap();
391 assert_eq!(&*py_backed_str, "hello");
392 });
393 }
394
395 #[test]
396 fn py_backed_str_into_pyobject() {
397 Python::with_gil(|py| {
398 let orig_str = PyString::new(py, "hello");
399 let py_backed_str = orig_str.extract::<PyBackedStr>().unwrap();
400 let new_str = py_backed_str.into_pyobject(py).unwrap();
401 assert_eq!(new_str.extract::<PyBackedStr>().unwrap(), "hello");
402 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
403 assert!(new_str.is(&orig_str));
404 });
405 }
406
407 #[test]
408 #[allow(deprecated)]
409 fn py_backed_str_into_py() {
410 Python::with_gil(|py| {
411 let orig_str = PyString::new(py, "hello");
412 let py_backed_str = orig_str.extract::<PyBackedStr>().unwrap();
413 let new_str = py_backed_str.into_py(py);
414 assert_eq!(new_str.extract::<PyBackedStr>(py).unwrap(), "hello");
415 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
416 assert!(new_str.is(&orig_str));
417 });
418 }
419
420 #[test]
421 fn py_backed_bytes_empty() {
422 Python::with_gil(|py| {
423 let b = PyBytes::new(py, b"");
424 let py_backed_bytes = b.extract::<PyBackedBytes>().unwrap();
425 assert_eq!(&*py_backed_bytes, b"");
426 });
427 }
428
429 #[test]
430 fn py_backed_bytes() {
431 Python::with_gil(|py| {
432 let b = PyBytes::new(py, b"abcde");
433 let py_backed_bytes = b.extract::<PyBackedBytes>().unwrap();
434 assert_eq!(&*py_backed_bytes, b"abcde");
435 });
436 }
437
438 #[test]
439 fn py_backed_bytes_from_bytes() {
440 Python::with_gil(|py| {
441 let b = PyBytes::new(py, b"abcde");
442 let py_backed_bytes = PyBackedBytes::from(b);
443 assert_eq!(&*py_backed_bytes, b"abcde");
444 });
445 }
446
447 #[test]
448 fn py_backed_bytes_from_bytearray() {
449 Python::with_gil(|py| {
450 let b = PyByteArray::new(py, b"abcde");
451 let py_backed_bytes = PyBackedBytes::from(b);
452 assert_eq!(&*py_backed_bytes, b"abcde");
453 });
454 }
455
456 #[test]
457 #[allow(deprecated)]
458 fn py_backed_bytes_into_pyobject() {
459 Python::with_gil(|py| {
460 let orig_bytes = PyBytes::new(py, b"abcde");
461 let py_backed_bytes = PyBackedBytes::from(orig_bytes.clone());
462 assert!((&py_backed_bytes)
463 .into_pyobject(py)
464 .unwrap()
465 .is(&orig_bytes));
466 assert!(py_backed_bytes.into_py(py).is(&orig_bytes));
467 });
468 }
469
470 #[test]
471 #[allow(deprecated)]
472 fn rust_backed_bytes_into_pyobject() {
473 Python::with_gil(|py| {
474 let orig_bytes = PyByteArray::new(py, b"abcde");
475 let rust_backed_bytes = PyBackedBytes::from(orig_bytes);
476 assert!(matches!(
477 rust_backed_bytes.storage,
478 PyBackedBytesStorage::Rust(_)
479 ));
480 let to_object = (&rust_backed_bytes).into_pyobject(py).unwrap();
481 assert!(&to_object.is_exact_instance_of::<PyBytes>());
482 assert_eq!(&to_object.extract::<PyBackedBytes>().unwrap(), b"abcde");
483 let into_py = rust_backed_bytes.into_py(py).into_bound(py);
484 assert!(&into_py.is_exact_instance_of::<PyBytes>());
485 assert_eq!(&into_py.extract::<PyBackedBytes>().unwrap(), b"abcde");
486 });
487 }
488
489 #[test]
490 fn test_backed_types_send_sync() {
491 fn is_send<T: Send>() {}
492 fn is_sync<T: Sync>() {}
493
494 is_send::<PyBackedStr>();
495 is_sync::<PyBackedStr>();
496
497 is_send::<PyBackedBytes>();
498 is_sync::<PyBackedBytes>();
499 }
500
501 #[cfg(feature = "py-clone")]
502 #[test]
503 fn test_backed_str_clone() {
504 Python::with_gil(|py| {
505 let s1: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
506 let s2 = s1.clone();
507 assert_eq!(s1, s2);
508
509 drop(s1);
510 assert_eq!(s2, "hello");
511 });
512 }
513
514 #[test]
515 fn test_backed_str_eq() {
516 Python::with_gil(|py| {
517 let s1: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
518 let s2: PyBackedStr = PyString::new(py, "hello").try_into().unwrap();
519 assert_eq!(s1, "hello");
520 assert_eq!(s1, s2);
521
522 let s3: PyBackedStr = PyString::new(py, "abcde").try_into().unwrap();
523 assert_eq!("abcde", s3);
524 assert_ne!(s1, s3);
525 });
526 }
527
528 #[test]
529 fn test_backed_str_hash() {
530 Python::with_gil(|py| {
531 let h = {
532 let mut hasher = DefaultHasher::new();
533 "abcde".hash(&mut hasher);
534 hasher.finish()
535 };
536
537 let s1: PyBackedStr = PyString::new(py, "abcde").try_into().unwrap();
538 let h1 = {
539 let mut hasher = DefaultHasher::new();
540 s1.hash(&mut hasher);
541 hasher.finish()
542 };
543
544 assert_eq!(h, h1);
545 });
546 }
547
548 #[test]
549 fn test_backed_str_ord() {
550 Python::with_gil(|py| {
551 let mut a = vec!["a", "c", "d", "b", "f", "g", "e"];
552 let mut b = a
553 .iter()
554 .map(|s| PyString::new(py, s).try_into().unwrap())
555 .collect::<Vec<PyBackedStr>>();
556
557 a.sort();
558 b.sort();
559
560 assert_eq!(a, b);
561 })
562 }
563
564 #[cfg(feature = "py-clone")]
565 #[test]
566 fn test_backed_bytes_from_bytes_clone() {
567 Python::with_gil(|py| {
568 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
569 let b2 = b1.clone();
570 assert_eq!(b1, b2);
571
572 drop(b1);
573 assert_eq!(b2, b"abcde");
574 });
575 }
576
577 #[cfg(feature = "py-clone")]
578 #[test]
579 fn test_backed_bytes_from_bytearray_clone() {
580 Python::with_gil(|py| {
581 let b1: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
582 let b2 = b1.clone();
583 assert_eq!(b1, b2);
584
585 drop(b1);
586 assert_eq!(b2, b"abcde");
587 });
588 }
589
590 #[test]
591 fn test_backed_bytes_eq() {
592 Python::with_gil(|py| {
593 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
594 let b2: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
595
596 assert_eq!(b1, b"abcde");
597 assert_eq!(b1, b2);
598
599 let b3: PyBackedBytes = PyBytes::new(py, b"hello").into();
600 assert_eq!(b"hello", b3);
601 assert_ne!(b1, b3);
602 });
603 }
604
605 #[test]
606 fn test_backed_bytes_hash() {
607 Python::with_gil(|py| {
608 let h = {
609 let mut hasher = DefaultHasher::new();
610 b"abcde".hash(&mut hasher);
611 hasher.finish()
612 };
613
614 let b1: PyBackedBytes = PyBytes::new(py, b"abcde").into();
615 let h1 = {
616 let mut hasher = DefaultHasher::new();
617 b1.hash(&mut hasher);
618 hasher.finish()
619 };
620
621 let b2: PyBackedBytes = PyByteArray::new(py, b"abcde").into();
622 let h2 = {
623 let mut hasher = DefaultHasher::new();
624 b2.hash(&mut hasher);
625 hasher.finish()
626 };
627
628 assert_eq!(h, h1);
629 assert_eq!(h, h2);
630 });
631 }
632
633 #[test]
634 fn test_backed_bytes_ord() {
635 Python::with_gil(|py| {
636 let mut a = vec![b"a", b"c", b"d", b"b", b"f", b"g", b"e"];
637 let mut b = a
638 .iter()
639 .map(|&b| PyBytes::new(py, b).into())
640 .collect::<Vec<PyBackedBytes>>();
641
642 a.sort();
643 b.sort();
644
645 assert_eq!(a, b);
646 })
647 }
648}