/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zerovec-0.11.5/src/cow.rs
Line | Count | Source |
1 | | // This file is part of ICU4X. For terms of use, please see the file |
2 | | // called LICENSE at the top level of the ICU4X source tree |
3 | | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | | |
5 | | use crate::ule::{EncodeAsVarULE, UleError, VarULE}; |
6 | | #[cfg(feature = "alloc")] |
7 | | use alloc::boxed::Box; |
8 | | use core::fmt; |
9 | | use core::marker::PhantomData; |
10 | | #[cfg(feature = "alloc")] |
11 | | use core::mem::ManuallyDrop; |
12 | | use core::ops::Deref; |
13 | | use core::ptr::NonNull; |
14 | | use zerofrom::ZeroFrom; |
15 | | |
16 | | /// Copy-on-write type that efficiently represents [`VarULE`] types as their bitstream representation. |
17 | | /// |
18 | | /// The primary use case for [`VarULE`] types is the ability to store complex variable-length datastructures |
19 | | /// inside variable-length collections like [`crate::VarZeroVec`]. |
20 | | /// |
21 | | /// Underlying this ability is the fact that [`VarULE`] types can be efficiently represented as a flat |
22 | | /// bytestream. |
23 | | /// |
24 | | /// In zero-copy cases, sometimes one wishes to unconditionally use this bytestream representation, for example |
25 | | /// to save stack size. A struct with five `Cow<'a, str>`s is not as stack-efficient as a single `Cow` containing |
26 | | /// the bytestream representation of, say, `Tuple5VarULE<str, str, str, str, str>`. |
27 | | /// |
28 | | /// This type helps in this case: It is logically a `Cow<'a, V>`, with some optimizations, that is guaranteed |
29 | | /// to serialize as a byte stream in machine-readable scenarios. |
30 | | /// |
31 | | /// During human-readable serialization, it will fall back to the serde impls on `V`, which ought to have |
32 | | /// a human-readable variant. |
33 | | pub struct VarZeroCow<'a, V: ?Sized> { |
34 | | /// Safety invariant: Contained slice must be a valid V |
35 | | /// It may or may not have a lifetime valid for 'a, it must be valid for as long as this type is around. |
36 | | raw: RawVarZeroCow, |
37 | | marker1: PhantomData<&'a V>, |
38 | | #[cfg(feature = "alloc")] |
39 | | marker2: PhantomData<Box<V>>, |
40 | | } |
41 | | |
42 | | /// VarZeroCow without the `V` to simulate a dropck eyepatch |
43 | | /// (i.e., prove to rustc that the dtor is not able to observe V or 'a) |
44 | | /// |
45 | | /// This is effectively `Cow<'a, [u8]>`, with the lifetime managed externally |
46 | | struct RawVarZeroCow { |
47 | | /// Pointer to data |
48 | | /// |
49 | | /// # Safety Invariants |
50 | | /// |
51 | | /// 1. This slice must always be valid as a byte slice |
52 | | /// 2. If `owned` is true, this slice can be freed. |
53 | | /// 3. VarZeroCow, the only user of this type, will impose an additional invariant that the buffer is a valid V |
54 | | buf: NonNull<[u8]>, |
55 | | /// The buffer is `Box<[u8]>` if true |
56 | | #[cfg(feature = "alloc")] |
57 | | owned: bool, |
58 | | // Safety: We do not need any PhantomDatas here, since the Drop impl does not observe borrowed data |
59 | | // if there is any. |
60 | | } |
61 | | |
62 | | #[cfg(feature = "alloc")] |
63 | | impl Drop for RawVarZeroCow { |
64 | | fn drop(&mut self) { |
65 | | // Note: this drop impl NEVER observes borrowed data (which may have already been cleaned up by the time the impl is called) |
66 | | if self.owned { |
67 | | unsafe { |
68 | | // Safety: (Invariant 2 on buf) |
69 | | // since owned is true, this is a valid Box<[u8]> and can be cleaned up |
70 | | let _ = Box::<[u8]>::from_raw(self.buf.as_ptr()); |
71 | | } |
72 | | } |
73 | | } |
74 | | } |
75 | | |
76 | | // This is mostly just a `Cow<[u8]>`, safe to implement Send and Sync on |
77 | | unsafe impl Send for RawVarZeroCow {} |
78 | | unsafe impl Sync for RawVarZeroCow {} |
79 | | |
80 | | impl Clone for RawVarZeroCow { |
81 | 0 | fn clone(&self) -> Self { |
82 | | #[cfg(feature = "alloc")] |
83 | | if self.is_owned() { |
84 | | // This clones the box |
85 | | let b: Box<[u8]> = self.as_bytes().into(); |
86 | | let b = ManuallyDrop::new(b); |
87 | | let buf: NonNull<[u8]> = (&**b).into(); |
88 | | return Self { |
89 | | // Invariants upheld: |
90 | | // 1 & 3: The bytes came from `self` so they're a valid value and byte slice |
91 | | // 2: This is owned (we cloned it), so we set owned to true. |
92 | | buf, |
93 | | owned: true, |
94 | | }; |
95 | | } |
96 | | // Unfortunately we can't just use `new_borrowed(self.deref())` since the lifetime is shorter |
97 | 0 | Self { |
98 | 0 | // Invariants upheld: |
99 | 0 | // 1 & 3: The bytes came from `self` so they're a valid value and byte slice |
100 | 0 | // 2: This is borrowed (we're sharing a borrow), so we set owned to false. |
101 | 0 | buf: self.buf, |
102 | 0 | #[cfg(feature = "alloc")] |
103 | 0 | owned: false, |
104 | 0 | } |
105 | 0 | } |
106 | | } |
107 | | |
108 | | impl<'a, V: ?Sized> Clone for VarZeroCow<'a, V> { |
109 | 0 | fn clone(&self) -> Self { |
110 | 0 | let raw = self.raw.clone(); |
111 | | // Invariant upheld: raw came from a valid VarZeroCow, so it |
112 | | // is a valid V |
113 | 0 | unsafe { Self::from_raw(raw) } |
114 | 0 | } |
115 | | } |
116 | | |
117 | | impl<'a, V: VarULE + ?Sized> VarZeroCow<'a, V> { |
118 | | /// Construct from a slice. Errors if the slice doesn't represent a valid `V` |
119 | 0 | pub fn parse_bytes(bytes: &'a [u8]) -> Result<Self, UleError> { |
120 | 0 | let val = V::parse_bytes(bytes)?; |
121 | 0 | Ok(Self::new_borrowed(val)) |
122 | 0 | } |
123 | | |
124 | | /// Construct from an owned slice. Errors if the slice doesn't represent a valid `V` |
125 | | /// |
126 | | /// ✨ *Enabled with the `alloc` Cargo feature.* |
127 | | #[cfg(feature = "alloc")] |
128 | | pub fn parse_owned_bytes(bytes: Box<[u8]>) -> Result<Self, UleError> { |
129 | | V::validate_bytes(&bytes)?; |
130 | | let bytes = ManuallyDrop::new(bytes); |
131 | | let buf: NonNull<[u8]> = (&**bytes).into(); |
132 | | let raw = RawVarZeroCow { |
133 | | // Invariants upheld: |
134 | | // 1 & 3: The bytes came from `val` so they're a valid value and byte slice |
135 | | // 2: This is owned, so we set owned to true. |
136 | | buf, |
137 | | owned: true, |
138 | | }; |
139 | | Ok(Self { |
140 | | raw, |
141 | | marker1: PhantomData, |
142 | | #[cfg(feature = "alloc")] |
143 | | marker2: PhantomData, |
144 | | }) |
145 | | } |
146 | | |
147 | | /// Construct from a slice that is known to represent a valid `V` |
148 | | /// |
149 | | /// # Safety |
150 | | /// |
151 | | /// `bytes` must be a valid `V`, i.e. it must successfully pass through |
152 | | /// `V::parse_bytes()` or `V::validate_bytes()`. |
153 | 0 | pub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self { |
154 | | unsafe { |
155 | | // Safety: bytes is an &T which is always non-null |
156 | 0 | let buf: NonNull<[u8]> = NonNull::new_unchecked(bytes as *const [u8] as *mut [u8]); |
157 | 0 | let raw = RawVarZeroCow { |
158 | 0 | // Invariants upheld: |
159 | 0 | // 1 & 3: Passed upstream to caller |
160 | 0 | // 2: This is borrowed, so we set owned to false. |
161 | 0 | buf, |
162 | 0 | #[cfg(feature = "alloc")] |
163 | 0 | owned: false, |
164 | 0 | }; |
165 | | // Invariant passed upstream to caller |
166 | 0 | Self::from_raw(raw) |
167 | | } |
168 | 0 | } |
169 | | |
170 | | /// Construct this from an [`EncodeAsVarULE`] version of the contained type |
171 | | /// |
172 | | /// Will always construct an owned version |
173 | | /// |
174 | | /// ✨ *Enabled with the `alloc` Cargo feature.* |
175 | | #[cfg(feature = "alloc")] |
176 | | pub fn from_encodeable<E: EncodeAsVarULE<V>>(encodeable: &E) -> Self { |
177 | | let b = crate::ule::encode_varule_to_box(encodeable); |
178 | | Self::new_owned(b) |
179 | | } |
180 | | |
181 | | /// Construct a new borrowed version of this |
182 | 0 | pub fn new_borrowed(val: &'a V) -> Self { |
183 | | unsafe { |
184 | | // Safety: val is a valid V, by type |
185 | 0 | Self::from_bytes_unchecked(val.as_bytes()) |
186 | | } |
187 | 0 | } |
188 | | |
189 | | /// Construct a new borrowed version of this |
190 | | /// |
191 | | /// ✨ *Enabled with the `alloc` Cargo feature.* |
192 | | #[cfg(feature = "alloc")] |
193 | | pub fn new_owned(val: Box<V>) -> Self { |
194 | | let val = ManuallyDrop::new(val); |
195 | | let buf: NonNull<[u8]> = val.as_bytes().into(); |
196 | | let raw = RawVarZeroCow { |
197 | | // Invariants upheld: |
198 | | // 1 & 3: The bytes came from `val` so they're a valid value and byte slice |
199 | | // 2: This is owned, so we set owned to true. |
200 | | buf, |
201 | | #[cfg(feature = "alloc")] |
202 | | owned: true, |
203 | | }; |
204 | | // The bytes came from `val`, so it's a valid value |
205 | | unsafe { Self::from_raw(raw) } |
206 | | } |
207 | | } |
208 | | |
209 | | impl<'a, V: ?Sized> VarZeroCow<'a, V> { |
210 | | /// Whether or not this is owned |
211 | 0 | pub fn is_owned(&self) -> bool { |
212 | 0 | self.raw.is_owned() |
213 | 0 | } |
214 | | |
215 | | /// Get the byte representation of this type |
216 | | /// |
217 | | /// Is also always a valid `V` and can be passed to |
218 | | /// `V::from_bytes_unchecked()` |
219 | 0 | pub fn as_bytes(&self) -> &[u8] { |
220 | | // The valid V invariant comes from Invariant 2 |
221 | 0 | self.raw.as_bytes() |
222 | 0 | } |
223 | | |
224 | | /// Invariant: `raw` must wrap a valid V, either owned or borrowed for 'a |
225 | 0 | const unsafe fn from_raw(raw: RawVarZeroCow) -> Self { |
226 | 0 | Self { |
227 | 0 | // Invariant passed up to caller |
228 | 0 | raw, |
229 | 0 | marker1: PhantomData, |
230 | 0 | #[cfg(feature = "alloc")] |
231 | 0 | marker2: PhantomData, |
232 | 0 | } |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl RawVarZeroCow { |
237 | | /// Whether or not this is owned |
238 | | #[inline] |
239 | 0 | pub fn is_owned(&self) -> bool { |
240 | | #[cfg(feature = "alloc")] |
241 | | return self.owned; |
242 | | #[cfg(not(feature = "alloc"))] |
243 | 0 | return false; |
244 | 0 | } |
245 | | |
246 | | /// Get the byte representation of this type |
247 | | #[inline] |
248 | 0 | pub fn as_bytes(&self) -> &[u8] { |
249 | | // Safety: Invariant 1 on self.buf |
250 | 0 | unsafe { self.buf.as_ref() } |
251 | 0 | } |
252 | | } |
253 | | |
254 | | impl<'a, V: VarULE + ?Sized> Deref for VarZeroCow<'a, V> { |
255 | | type Target = V; |
256 | 0 | fn deref(&self) -> &V { |
257 | | // Safety: From invariant 2 on self.buf |
258 | 0 | unsafe { V::from_bytes_unchecked(self.as_bytes()) } |
259 | 0 | } |
260 | | } |
261 | | |
262 | | impl<'a, V: VarULE + ?Sized> From<&'a V> for VarZeroCow<'a, V> { |
263 | 0 | fn from(other: &'a V) -> Self { |
264 | 0 | Self::new_borrowed(other) |
265 | 0 | } |
266 | | } |
267 | | |
268 | | #[cfg(feature = "alloc")] |
269 | | impl<'a, V: VarULE + ?Sized> From<Box<V>> for VarZeroCow<'a, V> { |
270 | | fn from(other: Box<V>) -> Self { |
271 | | Self::new_owned(other) |
272 | | } |
273 | | } |
274 | | |
275 | | impl<'a, V: VarULE + ?Sized + fmt::Debug> fmt::Debug for VarZeroCow<'a, V> { |
276 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
277 | 0 | self.deref().fmt(f) |
278 | 0 | } |
279 | | } |
280 | | |
281 | | // We need manual impls since `#[derive()]` is disallowed on packed types |
282 | | impl<'a, V: VarULE + ?Sized + PartialEq> PartialEq for VarZeroCow<'a, V> { |
283 | 0 | fn eq(&self, other: &Self) -> bool { |
284 | 0 | self.deref().eq(other.deref()) |
285 | 0 | } |
286 | | } |
287 | | |
288 | | impl<'a, V: VarULE + ?Sized + Eq> Eq for VarZeroCow<'a, V> {} |
289 | | |
290 | | impl<'a, V: VarULE + ?Sized + PartialOrd> PartialOrd for VarZeroCow<'a, V> { |
291 | 0 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { |
292 | 0 | self.deref().partial_cmp(other.deref()) |
293 | 0 | } |
294 | | } |
295 | | |
296 | | impl<'a, V: VarULE + ?Sized + Ord> Ord for VarZeroCow<'a, V> { |
297 | 0 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { |
298 | 0 | self.deref().cmp(other.deref()) |
299 | 0 | } |
300 | | } |
301 | | |
302 | | // # Safety |
303 | | // |
304 | | // encode_var_ule_len: Produces the length of the contained bytes, which are known to be a valid V by invariant |
305 | | // |
306 | | // encode_var_ule_write: Writes the contained bytes, which are known to be a valid V by invariant |
307 | | unsafe impl<'a, V: VarULE + ?Sized> EncodeAsVarULE<V> for VarZeroCow<'a, V> { |
308 | 0 | fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { |
309 | | // unnecessary if the other two are implemented |
310 | 0 | unreachable!() |
311 | | } |
312 | | |
313 | | #[inline] |
314 | 0 | fn encode_var_ule_len(&self) -> usize { |
315 | 0 | self.as_bytes().len() |
316 | 0 | } |
317 | | |
318 | | #[inline] |
319 | 0 | fn encode_var_ule_write(&self, dst: &mut [u8]) { |
320 | 0 | dst.copy_from_slice(self.as_bytes()) |
321 | 0 | } |
322 | | } |
323 | | |
324 | | #[cfg(feature = "serde")] |
325 | | impl<'a, V: VarULE + ?Sized + serde::Serialize> serde::Serialize for VarZeroCow<'a, V> { |
326 | | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
327 | | where |
328 | | S: serde::Serializer, |
329 | | { |
330 | | if serializer.is_human_readable() { |
331 | | <V as serde::Serialize>::serialize(self.deref(), serializer) |
332 | | } else { |
333 | | serializer.serialize_bytes(self.as_bytes()) |
334 | | } |
335 | | } |
336 | | } |
337 | | |
338 | | #[cfg(all(feature = "serde", feature = "alloc"))] |
339 | | impl<'a, 'de: 'a, V: VarULE + ?Sized> serde::Deserialize<'de> for VarZeroCow<'a, V> |
340 | | where |
341 | | Box<V>: serde::Deserialize<'de>, |
342 | | { |
343 | | fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> |
344 | | where |
345 | | Des: serde::Deserializer<'de>, |
346 | | { |
347 | | if deserializer.is_human_readable() { |
348 | | let b = Box::<V>::deserialize(deserializer)?; |
349 | | Ok(Self::new_owned(b)) |
350 | | } else { |
351 | | let bytes = <&[u8]>::deserialize(deserializer)?; |
352 | | Self::parse_bytes(bytes).map_err(serde::de::Error::custom) |
353 | | } |
354 | | } |
355 | | } |
356 | | |
357 | | #[cfg(feature = "databake")] |
358 | | impl<'a, V: VarULE + ?Sized> databake::Bake for VarZeroCow<'a, V> { |
359 | | fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream { |
360 | | env.insert("zerovec"); |
361 | | let bytes = self.as_bytes().bake(env); |
362 | | databake::quote! { |
363 | | // Safety: Known to come from a valid V since self.as_bytes() is always a valid V |
364 | | unsafe { |
365 | | zerovec::VarZeroCow::from_bytes_unchecked(#bytes) |
366 | | } |
367 | | } |
368 | | } |
369 | | } |
370 | | |
371 | | #[cfg(feature = "databake")] |
372 | | impl<'a, V: VarULE + ?Sized> databake::BakeSize for VarZeroCow<'a, V> { |
373 | | fn borrows_size(&self) -> usize { |
374 | | self.as_bytes().len() |
375 | | } |
376 | | } |
377 | | |
378 | | impl<'a, V: VarULE + ?Sized> ZeroFrom<'a, V> for VarZeroCow<'a, V> { |
379 | | #[inline] |
380 | 0 | fn zero_from(other: &'a V) -> Self { |
381 | 0 | Self::new_borrowed(other) |
382 | 0 | } |
383 | | } |
384 | | |
385 | | impl<'a, 'b, V: VarULE + ?Sized> ZeroFrom<'a, VarZeroCow<'b, V>> for VarZeroCow<'a, V> { |
386 | | #[inline] |
387 | 0 | fn zero_from(other: &'a VarZeroCow<'b, V>) -> Self { |
388 | 0 | Self::new_borrowed(other) |
389 | 0 | } |
390 | | } |
391 | | |
392 | | #[cfg(test)] |
393 | | mod tests { |
394 | | use super::VarZeroCow; |
395 | | use crate::ule::tuplevar::Tuple3VarULE; |
396 | | use crate::vecs::VarZeroSlice; |
397 | | #[test] |
398 | | fn test_cow_roundtrip() { |
399 | | type Messy = Tuple3VarULE<str, [u8], VarZeroSlice<str>>; |
400 | | let vec = vec!["one", "two", "three"]; |
401 | | let messy: VarZeroCow<Messy> = |
402 | | VarZeroCow::from_encodeable(&("hello", &b"g\xFF\xFFdbye"[..], vec)); |
403 | | |
404 | | assert_eq!(messy.a(), "hello"); |
405 | | assert_eq!(messy.b(), b"g\xFF\xFFdbye"); |
406 | | assert_eq!(&messy.c()[1], "two"); |
407 | | |
408 | | #[cfg(feature = "serde")] |
409 | | { |
410 | | let bincode = bincode::serialize(&messy).unwrap(); |
411 | | let deserialized: VarZeroCow<Messy> = bincode::deserialize(&bincode).unwrap(); |
412 | | assert_eq!( |
413 | | messy, deserialized, |
414 | | "Single element roundtrips with bincode" |
415 | | ); |
416 | | assert!(!deserialized.is_owned()); |
417 | | |
418 | | let json = serde_json::to_string(&messy).unwrap(); |
419 | | let deserialized: VarZeroCow<Messy> = serde_json::from_str(&json).unwrap(); |
420 | | assert_eq!(messy, deserialized, "Single element roundtrips with serde"); |
421 | | } |
422 | | } |
423 | | |
424 | | struct TwoCows<'a> { |
425 | | cow1: VarZeroCow<'a, str>, |
426 | | cow2: VarZeroCow<'a, str>, |
427 | | } |
428 | | |
429 | | #[test] |
430 | | fn test_eyepatch_works() { |
431 | | // This code should compile |
432 | | let mut two = TwoCows { |
433 | | cow1: VarZeroCow::new_borrowed("hello"), |
434 | | cow2: VarZeroCow::new_owned("world".into()), |
435 | | }; |
436 | | let three = VarZeroCow::new_borrowed(&*two.cow2); |
437 | | two.cow1 = three; |
438 | | |
439 | | // Without the eyepatch, dropck will be worried that the dtor of two.cow1 can observe the |
440 | | // data it borrowed from two.cow2, which may have already been deleted |
441 | | |
442 | | // This test will fail if you add an empty `impl<'a, V: ?Sized> Drop for VarZeroCow<'a, V>` |
443 | | } |
444 | | } |