compact_str/features/
smallvec.rs

1use smallvec::SmallVec;
2
3use crate::repr::MAX_SIZE;
4use crate::CompactString;
5
6impl CompactString {
7    /// Converts a [`CompactString`] into a byte vector
8    ///
9    /// This consumes the [`CompactString`] and returns a [`SmallVec`], so we do not need to copy
10    /// contents
11    ///
12    /// Note: [`SmallVec`] is an inline-able version [`Vec`](alloc::vec::Vec), just like
13    /// [`CompactString`] is an inline-able version of [`String`](alloc::string::String).
14    ///
15    /// # Example
16    /// ```
17    /// use compact_str::CompactString;
18    ///
19    /// let c = CompactString::new("hello");
20    /// let bytes = c.into_bytes();
21    ///
22    /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
23    /// ```
24    #[cfg_attr(docsrs, doc(cfg(feature = "smallvec")))]
25    pub fn into_bytes(self) -> SmallVec<[u8; MAX_SIZE]> {
26        self.0.into_bytes()
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use alloc::string::String;
33
34    use proptest::prelude::*;
35    use test_strategy::proptest;
36
37    use crate::repr::MAX_SIZE;
38    use crate::tests::rand_unicode;
39    use crate::CompactString;
40
41    /// generates random unicode strings, that are at least MAX_SIZE bytes long
42    pub fn rand_long_unicode() -> impl Strategy<Value = String> {
43        proptest::collection::vec(proptest::char::any(), (MAX_SIZE + 1)..80)
44            .prop_map(|v| v.into_iter().collect())
45    }
46
47    #[test]
48    fn test_buffer_reuse() {
49        let c = CompactString::from("I am a longer string that will be on the heap");
50        let c_ptr = c.as_ptr();
51
52        let bytes = c.into_bytes();
53        let b_ptr = bytes.as_ptr();
54
55        // Note: inlined CompactStrings also get their buffers re-used, but we can't assert their
56        // re-use the same way we do for longer strings, because the underlying array may move on
57        // the callstack, whereas for longer strings the buffer is not moving on the heap
58
59        // converting into_bytes should _always_ re-use the underlying buffer
60        assert_eq!(c_ptr, b_ptr);
61    }
62
63    #[proptest]
64    #[cfg_attr(miri, ignore)]
65    fn proptest_buffer_reuse(#[strategy(rand_long_unicode())] s: String) {
66        let c = CompactString::from(s);
67        let c_ptr = c.as_ptr();
68
69        let bytes = c.into_bytes();
70        let b_ptr = bytes.as_ptr();
71
72        // converting into_bytes should _always_ re-use the underlying buffer
73        prop_assert_eq!(c_ptr, b_ptr);
74    }
75
76    #[proptest]
77    #[cfg_attr(miri, ignore)]
78    fn proptest_roundtrip(#[strategy(rand_unicode())] s: String) {
79        let og_compact = CompactString::from(s.clone());
80        prop_assert_eq!(&og_compact, &s);
81
82        let bytes = og_compact.into_bytes();
83
84        let ex_compact = CompactString::from_utf8(bytes).unwrap();
85        prop_assert_eq!(&ex_compact, &s);
86    }
87}