beef/
lib.rs

1//! Faster, more compact implementation of `Cow`.
2//!
3//! **[Changelog](https://github.com/maciejhirsz/beef/releases) -**
4//! **[Cargo](https://crates.io/crates/beef) -**
5//! **[Repository](https://github.com/maciejhirsz/beef)**
6//!
7//! ```rust
8//! use beef::Cow;
9//!
10//! let borrowed: Cow<str> = Cow::borrowed("Hello");
11//! let owned: Cow<str> = Cow::owned(String::from("World"));
12//!
13//! assert_eq!(
14//!     format!("{} {}!", borrowed, owned),
15//!     "Hello World!",
16//! );
17//! ```
18//!
19//! There are two versions of `Cow` exposed by this crate:
20//!
21//! + `beef::Cow` is 3 words wide: pointer, length, and capacity. It stores the ownership tag in capacity.
22//! + `beef::lean::Cow` is 2 words wide, storing length, capacity, and the ownership tag all in one word.
23//!
24//! Both versions are leaner than the `std::borrow::Cow`:
25//!
26//! ```rust
27//! use std::mem::size_of;
28//!
29//! const WORD: usize = size_of::<usize>();
30//!
31//! assert_eq!(size_of::<std::borrow::Cow<str>>(), 4 * WORD);
32//! assert_eq!(size_of::<beef::Cow<str>>(), 3 * WORD);
33//!
34//! // Lean variant is two words on 64-bit architecture
35//! #[cfg(target_pointer_width = "64")]
36//! assert_eq!(size_of::<beef::lean::Cow<str>>(), 2 * WORD);
37//! ```
38#![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))]
39#![warn(missing_docs)]
40#![cfg_attr(not(test), no_std)]
41extern crate alloc;
42
43mod traits;
44mod wide;
45
46#[cfg(feature = "impl_serde")]
47mod serde;
48
49pub mod generic;
50#[cfg(target_pointer_width = "64")]
51pub mod lean;
52
53#[cfg(not(target_pointer_width = "64"))]
54pub mod lean {
55    /// Re-exports 3-word Cow for non-64-bit targets
56    pub use super::wide::Cow;
57}
58
59pub use wide::Cow;
60
61#[rustfmt::skip]
62macro_rules! test { ($tmod:ident => $cow:path) => {
63    #[cfg(test)]
64    mod $tmod {
65        use $cow;
66
67        #[test]
68        fn borrowed_str() {
69            let s = "Hello World";
70            let c = Cow::borrowed(s);
71
72            assert_eq!(s, c);
73            assert_eq!(s, c.as_ref());
74            assert_eq!(s, &*c);
75        }
76
77        #[test]
78        fn owned_string() {
79            let s = String::from("Hello World");
80            let c: Cow<str> = Cow::owned(s.clone());
81
82            assert_eq!(s, c);
83        }
84
85        #[test]
86        fn into_owned() {
87            let hello = "Hello World";
88            let borrowed = Cow::borrowed(hello);
89            let owned: Cow<str> = Cow::owned(String::from(hello));
90
91            assert_eq!(borrowed.into_owned(), hello);
92            assert_eq!(owned.into_owned(), hello);
93        }
94
95        #[test]
96        fn borrowed_slice() {
97            let s: &[_] = &[1, 2, 42];
98            let c = Cow::borrowed(s);
99
100            assert_eq!(s, c);
101            assert_eq!(s, c.as_ref());
102            assert_eq!(s, &*c);
103        }
104
105        #[test]
106        fn owned_slice() {
107            let s = vec![1, 2, 42];
108            let c: Cow<[_]> = Cow::owned(s.clone());
109
110            assert_eq!(s, c);
111        }
112
113        #[test]
114        fn into_owned_vec() {
115            let hello: &[u8] = b"Hello World";
116            let borrowed = Cow::borrowed(hello);
117            let owned: Cow<[u8]> = Cow::owned(hello.to_vec());
118
119            assert_eq!(borrowed.into_owned(), hello);
120            assert_eq!(owned.into_owned(), hello);
121        }
122
123        #[test]
124        fn hash() {
125            use std::collections::hash_map::DefaultHasher;
126            use std::hash::{Hash, Hasher};
127
128            let slice = "Hello World!";
129            let borrowed = Cow::borrowed(slice);
130            let owned: Cow<str> = Cow::owned(slice.to_owned());
131
132            let hash1 = {
133                let mut hasher = DefaultHasher::default();
134
135                slice.hash(&mut hasher);
136
137                hasher.finish()
138            };
139
140            let hash2 = {
141                let mut hasher = DefaultHasher::default();
142
143                borrowed.hash(&mut hasher);
144
145                hasher.finish()
146            };
147
148            let hash3 = {
149                let mut hasher = DefaultHasher::default();
150
151                owned.hash(&mut hasher);
152
153                hasher.finish()
154            };
155
156            assert_eq!(hash1, hash2);
157            assert_eq!(hash1, hash3);
158            assert_eq!(hash2, hash3);
159        }
160
161        #[test]
162        fn ord_and_partial_ord() {
163            use std::cmp::Ordering;
164
165            macro_rules! generate_order_tests {
166                ( $f:tt => $order:expr => $left:expr, $right:expr ) => {
167                    assert_eq!(
168                        Cow::<str>::borrowed($left).$f(&Cow::<str>::borrowed($right)),
169                        $order
170                    );
171
172                    assert_eq!(
173                        Cow::<str>::owned($left.to_owned())
174                            .$f(&Cow::<str>::borrowed($right)),
175                        $order
176                    );
177
178                    assert_eq!(
179                        Cow::<str>::borrowed($left)
180                            .$f(&Cow::<str>::owned($right.to_owned())),
181                        $order
182                    );
183
184                    assert_eq!(
185                        Cow::<str>::owned($left.to_owned())
186                            .$f(&Cow::<str>::owned($right.to_owned())),
187                        $order
188                    );
189                }
190            }
191
192            generate_order_tests!(partial_cmp => Some(Ordering::Equal) => "a", "a");
193            generate_order_tests!(partial_cmp => Some(Ordering::Less) => "a", "b");
194            generate_order_tests!(partial_cmp => Some(Ordering::Greater) => "b", "a");
195
196            generate_order_tests!(cmp => Ordering::Equal => "a", "a");
197            generate_order_tests!(cmp => Ordering::Less => "a", "b");
198            generate_order_tests!(cmp => Ordering::Greater => "b", "a");
199        }
200
201        #[test]
202        fn from_std_cow() {
203            let std = std::borrow::Cow::Borrowed("Hello World");
204            let beef = Cow::from(std.clone());
205
206            assert_eq!(&*std, &*beef);
207        }
208
209        #[test]
210        fn unwrap_borrowed() {
211            let borrowed = Cow::borrowed("Hello");
212
213            assert_eq!(borrowed.unwrap_borrowed(), "Hello");
214        }
215
216        #[test]
217        #[should_panic]
218        fn unwrap_owned() {
219            let borrowed: Cow<str> = Cow::owned("Hello".to_string());
220
221            borrowed.unwrap_borrowed();
222        }
223
224        #[test]
225        fn stress_test_owned() {
226            let mut expected = String::from("Hello... ");
227            let mut cow: Cow<str> = Cow::borrowed("Hello... ");
228
229            #[cfg(not(miri))]
230            let iterations = 1024;
231            #[cfg(miri)]
232            let iterations = 10;
233
234            for i in 0..iterations {
235                if i % 3 == 0 {
236                    let old = cow;
237                    cow = old.clone();
238
239                    std::mem::drop(old);
240                }
241
242                let mut owned = cow.into_owned();
243
244                expected.push_str("Hello?.. ");
245                owned.push_str("Hello?.. ");
246
247                cow = owned.into();
248            }
249
250            assert_eq!(expected, cow.into_owned());
251        }
252
253        #[test]
254        fn const_fn_str() {
255            const HELLO: Cow<str> = Cow::const_str("Hello");
256
257            assert_eq!(&*HELLO, "Hello");
258        }
259
260        #[test]
261        #[cfg(feature = "const_fn")]
262        fn const_fn_slice() {
263            const FOO: Cow<[u8]> = Cow::const_slice(b"bar");
264
265            assert_eq!(&*FOO, b"bar");
266        }
267
268        #[test]
269        fn default_str() {
270            let empty: Cow<str> = Default::default();
271
272            assert_eq!(&*empty, "");
273        }
274
275        #[test]
276        fn default_slice() {
277            let empty: Cow<[u8]> = Default::default();
278
279            assert_eq!(&*empty, b"");
280        }
281    }
282} }
283
284test!(test_wide => crate::wide::Cow);
285test!(test_lean => crate::lean::Cow);