| // This file is part of ICU4X. For terms of use, please see the file | ||
| // called LICENSE at the top level of the ICU4X source tree | ||
| // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). | ||
| //! Types for optional pointers with niche optimization. | ||
| //! | ||
| //! The main type is [`CartableOptionPointer`], which is like `Option<Rc>` but | ||
| //! with a niche so that the resulting `Yoke` has a niche. The following four | ||
| //! types can be stored in the `CartableOptionPointer`: | ||
| //! | ||
| //! 1. `&T` | ||
| //! 2. `Box<T>` | ||
| //! 3. `Rc<T>` | ||
| //! 4. `Arc<T>` | ||
| //! | ||
| //! These four types implement the sealed unsafe trait [`CartablePointerLike`]. | ||
| //! In addition, all except `Box<T>` impl [`CloneableCartablePointerLike`], | ||
| //! which allows [`CartableOptionPointer`] to implement `Clone`. | ||
| use crate::CloneableCart; | ||
| #[cfg(feature = "alloc")] | ||
| use alloc::boxed::Box; | ||
| #[cfg(feature = "alloc")] | ||
| use alloc::rc::Rc; | ||
| #[cfg(feature = "alloc")] | ||
| use alloc::sync::Arc; | ||
| #[cfg(test)] | ||
| use core::cell::Cell; | ||
| use core::marker::PhantomData; | ||
| use core::ptr::NonNull; | ||
| use stable_deref_trait::StableDeref; | ||
| #[inline] | ||
| fn sentinel_for<T>() -> NonNull<T> { | ||
| static SENTINEL: &u8 = &0x1a; // SUB | ||
| unsafe { NonNull::new_unchecked(SENTINEL as *const u8 as *mut T) } | ||
| } | ||
| #[cfg(test)] | ||
| thread_local! { | ||
| static DROP_INVOCATIONS: Cell<usize> = const { Cell::new(0) }; | ||
| } | ||
| mod private { | ||
| pub trait Sealed {} | ||
| } | ||
| use private::Sealed; | ||
| /// An object fully representable by a non-null pointer. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Implementer safety: | ||
| /// | ||
| /// 1. `into_raw` transfers ownership of the values referenced by StableDeref to the caller, | ||
| /// if there is ownership to transfer | ||
| /// 2. `drop_raw` returns ownership back to the impl, if there is ownership to transfer | ||
| /// 3. `into_raw` must not return the sentinel pointer | ||
| /// | ||
| /// Note: the pointer `NonNull<Self::Raw>` may or may not be aligned and it should never | ||
| /// be dereferenced. Rust allows unaligned pointers; see [`std::ptr::read_unaligned`]. | ||
| pub unsafe trait CartablePointerLike: StableDeref + Sealed { | ||
| /// The raw type used for [`Self::into_raw`] and [`Self::drop_raw`]. | ||
| #[doc(hidden)] | ||
| type Raw; | ||
| /// Converts this pointer-like into a pointer. | ||
| #[doc(hidden)] | ||
| fn into_raw(self) -> NonNull<Self::Raw>; | ||
| /// Drops any memory associated with this pointer-like. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Caller safety: | ||
| /// | ||
| /// 1. The pointer MUST have been returned by this impl's `into_raw`. | ||
| /// 2. The pointer MUST NOT be dangling. | ||
| #[doc(hidden)] | ||
| unsafe fn drop_raw(pointer: NonNull<Self::Raw>); | ||
| } | ||
| /// An object that implements [`CartablePointerLike`] that also | ||
| /// supports cloning without changing the address of referenced data. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Implementer safety: | ||
| /// | ||
| /// 1. `addref_raw` must create a new owner such that an additional call to | ||
| /// `drop_raw` does not create a dangling pointer | ||
| /// 2. `addref_raw` must not change the address of any referenced data. | ||
| pub unsafe trait CloneableCartablePointerLike: CartablePointerLike { | ||
| /// Clones this pointer-like. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Caller safety: | ||
| /// | ||
| /// 1. The pointer MUST have been returned by this impl's `into_raw`. | ||
| /// 2. The pointer MUST NOT be dangling. | ||
| #[doc(hidden)] | ||
| unsafe fn addref_raw(pointer: NonNull<Self::Raw>); | ||
| } | ||
| impl<'a, T> Sealed for &'a T {} | ||
| // Safety: | ||
| // 1. There is no ownership to transfer | ||
| // 2. There is no ownership to transfer | ||
| // 3. External clients do not have the sentinel address so it won't be used in this reference. | ||
| unsafe impl<'a, T> CartablePointerLike for &'a T { | ||
| type Raw = T; | ||
| #[inline] | ||
| fn into_raw(self) -> NonNull<T> { | ||
| self.into() | ||
| } | ||
| #[inline] | ||
| unsafe fn drop_raw(_pointer: NonNull<T>) { | ||
| // No-op: references are borrowed from elsewhere | ||
| } | ||
| } | ||
| // Safety: | ||
| // 1. There is no ownership | ||
| // 2. The impl is a no-op so no addresses are changed. | ||
| unsafe impl<'a, T> CloneableCartablePointerLike for &'a T { | ||
| #[inline] | ||
| unsafe fn addref_raw(_pointer: NonNull<T>) { | ||
| // No-op: references are borrowed from elsewhere | ||
| } | ||
| } | ||
| #[cfg(feature = "alloc")] | ||
| impl<T> Sealed for Box<T> {} | ||
| // Safety: | ||
| // 1. `Box::into_raw` says: "After calling this function, the caller is responsible for the | ||
| // memory previously managed by the Box." | ||
| // 2. `Box::from_raw` says: "After calling this function, the raw pointer is owned by the | ||
| // resulting Box." | ||
| // 3. The pointer comes from the Box so it won't be the sentinel pointer. | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<T> CartablePointerLike for Box<T> { | ||
| type Raw = T; | ||
| #[inline] | ||
| fn into_raw(self) -> NonNull<T> { | ||
| // Safety: `Box::into_raw` says: "The pointer will be properly aligned and non-null." | ||
| unsafe { NonNull::new_unchecked(Box::into_raw(self)) } | ||
| } | ||
| #[inline] | ||
| unsafe fn drop_raw(pointer: NonNull<T>) { | ||
| let _box = Box::from_raw(pointer.as_ptr()); | ||
| // Boxes are always dropped | ||
| #[cfg(test)] | ||
| DROP_INVOCATIONS.with(|x| x.set(x.get() + 1)) | ||
| } | ||
| } | ||
| #[cfg(feature = "alloc")] | ||
| impl<T> Sealed for Rc<T> {} | ||
| // Safety: | ||
| // 1. `Rc::into_raw` says: "Consumes the Rc, returning the wrapped pointer. To avoid a memory | ||
| // leak the pointer must be converted back to an Rc using Rc::from_raw." | ||
| // 2. See 1. | ||
| // 3. The pointer comes from the Rc so it won't be the sentinel pointer. | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<T> CartablePointerLike for Rc<T> { | ||
| type Raw = T; | ||
| #[inline] | ||
| fn into_raw(self) -> NonNull<T> { | ||
| // Safety: Rcs must contain data (and not be null) | ||
| unsafe { NonNull::new_unchecked(Rc::into_raw(self) as *mut T) } | ||
| } | ||
| #[inline] | ||
| unsafe fn drop_raw(pointer: NonNull<T>) { | ||
| let _rc = Rc::from_raw(pointer.as_ptr()); | ||
| // Rc is dropped if refcount is 1 | ||
| #[cfg(test)] | ||
| if Rc::strong_count(&_rc) == 1 { | ||
| DROP_INVOCATIONS.with(|x| x.set(x.get() + 1)) | ||
| } | ||
| } | ||
| } | ||
| // Safety: | ||
| // 1. The impl increases the refcount such that `Drop` will decrease it. | ||
| // 2. The impl increases refcount without changing the address of data. | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<T> CloneableCartablePointerLike for Rc<T> { | ||
| #[inline] | ||
| unsafe fn addref_raw(pointer: NonNull<T>) { | ||
| // Safety: The caller safety of this function says that: | ||
| // 1. The pointer was obtained through Rc::into_raw | ||
| // 2. The associated Rc instance is valid | ||
| // Further, this impl is not defined for anything but the global allocator. | ||
| Rc::increment_strong_count(pointer.as_ptr()); | ||
| } | ||
| } | ||
| #[cfg(feature = "alloc")] | ||
| impl<T> Sealed for Arc<T> {} | ||
| // Safety: | ||
| // 1. `Rc::into_raw` says: "Consumes the Arc, returning the wrapped pointer. To avoid a memory | ||
| // leak the pointer must be converted back to an Arc using Arc::from_raw." | ||
| // 2. See 1. | ||
| // 3. The pointer comes from the Arc so it won't be the sentinel pointer. | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<T> CartablePointerLike for Arc<T> { | ||
| type Raw = T; | ||
| #[inline] | ||
| fn into_raw(self) -> NonNull<T> { | ||
| // Safety: Arcs must contain data (and not be null) | ||
| unsafe { NonNull::new_unchecked(Arc::into_raw(self) as *mut T) } | ||
| } | ||
| #[inline] | ||
| unsafe fn drop_raw(pointer: NonNull<T>) { | ||
| let _arc = Arc::from_raw(pointer.as_ptr()); | ||
| // Arc is dropped if refcount is 1 | ||
| #[cfg(test)] | ||
| if Arc::strong_count(&_arc) == 1 { | ||
| DROP_INVOCATIONS.with(|x| x.set(x.get() + 1)) | ||
| } | ||
| } | ||
| } | ||
| // Safety: | ||
| // 1. The impl increases the refcount such that `Drop` will decrease it. | ||
| // 2. The impl increases refcount without changing the address of data. | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<T> CloneableCartablePointerLike for Arc<T> { | ||
| #[inline] | ||
| unsafe fn addref_raw(pointer: NonNull<T>) { | ||
| // Safety: The caller safety of this function says that: | ||
| // 1. The pointer was obtained through Arc::into_raw | ||
| // 2. The associated Arc instance is valid | ||
| // Further, this impl is not defined for anything but the global allocator. | ||
| Arc::increment_strong_count(pointer.as_ptr()); | ||
| } | ||
| } | ||
| /// A type with similar semantics as `Option<C<T>>` but with a niche. | ||
| /// | ||
| /// This type cannot be publicly constructed. To use this in a `Yoke`, see | ||
| /// [`Yoke::convert_cart_into_option_pointer`]. | ||
| /// | ||
| /// [`Yoke::convert_cart_into_option_pointer`]: crate::Yoke::convert_cart_into_option_pointer | ||
| #[derive(Debug)] | ||
| pub struct CartableOptionPointer<C> | ||
| where | ||
| C: CartablePointerLike, | ||
| { | ||
| /// The inner pointer. | ||
| /// | ||
| /// # Invariants | ||
| /// | ||
| /// 1. Must be either `SENTINEL_PTR` or created from `CartablePointerLike::into_raw` | ||
| /// 2. If non-sentinel, must _always_ be for a valid SelectedRc | ||
| inner: NonNull<C::Raw>, | ||
| _cartable: PhantomData<C>, | ||
| } | ||
| impl<C> CartableOptionPointer<C> | ||
| where | ||
| C: CartablePointerLike, | ||
| { | ||
| /// Creates a new instance corresponding to a `None` value. | ||
| #[inline] | ||
| pub(crate) fn none() -> Self { | ||
| Self { | ||
| inner: sentinel_for::<C::Raw>(), | ||
| _cartable: PhantomData, | ||
| } | ||
| } | ||
| /// Creates a new instance corresponding to a `Some` value. | ||
| #[inline] | ||
| pub(crate) fn from_cartable(cartable: C) -> Self { | ||
| Self { | ||
| inner: cartable.into_raw(), | ||
| _cartable: PhantomData, | ||
| } | ||
| } | ||
| /// Returns whether this instance is `None`. From the return value: | ||
| /// | ||
| /// - If `true`, the instance is `None` | ||
| /// - If `false`, the instance is a valid `SelectedRc` | ||
| #[inline] | ||
| pub fn is_none(&self) -> bool { | ||
| self.inner == sentinel_for::<C::Raw>() | ||
| } | ||
| } | ||
| impl<C> Drop for CartableOptionPointer<C> | ||
| where | ||
| C: CartablePointerLike, | ||
| { | ||
| #[inline] | ||
| fn drop(&mut self) { | ||
| let ptr = self.inner; | ||
| if ptr != sentinel_for::<C::Raw>() { | ||
| // By the invariants, `ptr` is a valid raw value since it's | ||
| // either that or sentinel, and we just checked for sentinel. | ||
| // We will replace it with the sentinel and then drop `ptr`. | ||
| self.inner = sentinel_for::<C::Raw>(); | ||
| // Safety: by the invariants, `ptr` is a valid raw value. | ||
| unsafe { C::drop_raw(ptr) } | ||
| } | ||
| } | ||
| } | ||
| impl<C> Clone for CartableOptionPointer<C> | ||
| where | ||
| C: CloneableCartablePointerLike, | ||
| { | ||
| #[inline] | ||
| fn clone(&self) -> Self { | ||
| let ptr = self.inner; | ||
| if ptr != sentinel_for::<C::Raw>() { | ||
| // By the invariants, `ptr` is a valid raw value since it's | ||
| // either that or sentinel, and we just checked for sentinel. | ||
| // Safety: by the invariants, `ptr` is a valid raw value. | ||
| unsafe { C::addref_raw(ptr) } | ||
| } | ||
| Self { | ||
| inner: self.inner, | ||
| _cartable: PhantomData, | ||
| } | ||
| } | ||
| } | ||
| // Safety: logically an Option<C>. Has same bounds as Option<C>. | ||
| // The `StableDeref` parts of `C` continue to be `StableDeref`. | ||
| unsafe impl<C> CloneableCart for CartableOptionPointer<C> where | ||
| C: CloneableCartablePointerLike + CloneableCart | ||
| { | ||
| } | ||
| // Safety: logically an Option<C>. Has same bounds as Option<C> | ||
| unsafe impl<C> Send for CartableOptionPointer<C> where C: Sync + CartablePointerLike {} | ||
| // Safety: logically an Option<C>. Has same bounds as Option<C> | ||
| unsafe impl<C> Sync for CartableOptionPointer<C> where C: Send + CartablePointerLike {} | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use crate::Yoke; | ||
| use core::mem::size_of; | ||
| const SAMPLE_BYTES: &[u8] = b"abCDEfg"; | ||
| const W: usize = size_of::<usize>(); | ||
| #[test] | ||
| fn test_sizes() { | ||
| assert_eq!(W * 4, size_of::<Yoke<[usize; 3], &&[u8]>>()); | ||
| assert_eq!(W * 4, size_of::<Yoke<[usize; 3], Option<&&[u8]>>>()); | ||
| assert_eq!( | ||
| W * 4, | ||
| size_of::<Yoke<[usize; 3], CartableOptionPointer<&&[u8]>>>() | ||
| ); | ||
| assert_eq!(W * 4, size_of::<Option<Yoke<[usize; 3], &&[u8]>>>()); | ||
| assert_eq!(W * 5, size_of::<Option<Yoke<[usize; 3], Option<&&[u8]>>>>()); | ||
| assert_eq!( | ||
| W * 4, | ||
| size_of::<Option<Yoke<[usize; 3], CartableOptionPointer<&&[u8]>>>>() | ||
| ); | ||
| } | ||
| #[test] | ||
| fn test_new_sentinel() { | ||
| let start = DROP_INVOCATIONS.with(Cell::get); | ||
| { | ||
| let _ = CartableOptionPointer::<Rc<&[u8]>>::none(); | ||
| } | ||
| assert_eq!(start, DROP_INVOCATIONS.with(Cell::get)); | ||
| { | ||
| let _ = CartableOptionPointer::<Rc<&[u8]>>::none(); | ||
| } | ||
| assert_eq!(start, DROP_INVOCATIONS.with(Cell::get)); | ||
| } | ||
| #[test] | ||
| fn test_new_rc() { | ||
| let start = DROP_INVOCATIONS.with(Cell::get); | ||
| { | ||
| let _ = CartableOptionPointer::<Rc<&[u8]>>::from_cartable(SAMPLE_BYTES.into()); | ||
| } | ||
| assert_eq!(start + 1, DROP_INVOCATIONS.with(Cell::get)); | ||
| } | ||
| #[test] | ||
| fn test_rc_clone() { | ||
| let start = DROP_INVOCATIONS.with(Cell::get); | ||
| { | ||
| let x = CartableOptionPointer::<Rc<&[u8]>>::from_cartable(SAMPLE_BYTES.into()); | ||
| assert_eq!(start, DROP_INVOCATIONS.with(Cell::get)); | ||
| { | ||
| let _ = x.clone(); | ||
| } | ||
| assert_eq!(start, DROP_INVOCATIONS.with(Cell::get)); | ||
| { | ||
| let _ = x.clone(); | ||
| let _ = x.clone(); | ||
| let _ = x.clone(); | ||
| } | ||
| assert_eq!(start, DROP_INVOCATIONS.with(Cell::get)); | ||
| } | ||
| assert_eq!(start + 1, DROP_INVOCATIONS.with(Cell::get)); | ||
| } | ||
| } |
| { | ||
| "git": { | ||
| "sha1": "8b080b3753b91c58113c47c99108185d0614499c" | ||
| "sha1": "55cd12ebb25c6261492e1e3dfa2e6453c54dde31" | ||
| }, | ||
| "path_in_vcs": "utils/yoke" | ||
| } |
+12
-7
@@ -16,3 +16,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "yoke" | ||
| version = "0.7.3" | ||
| version = "0.7.4" | ||
| authors = ["Manish Goregaokar <manishsmail@gmail.com>"] | ||
@@ -44,3 +44,3 @@ include = [ | ||
| ] | ||
| license-file = "LICENSE" | ||
| license = "Unicode-3.0" | ||
| repository = "https://github.com/unicode-org/icu4x" | ||
@@ -58,3 +58,3 @@ | ||
| [dependencies.serde] | ||
| version = "1.0" | ||
| version = "1.0.110" | ||
| optional = true | ||
@@ -68,3 +68,3 @@ default-features = false | ||
| [dependencies.yoke-derive] | ||
| version = "0.7.3" | ||
| version = "0.7.4" | ||
| optional = true | ||
@@ -74,3 +74,3 @@ default-features = false | ||
| [dependencies.zerofrom] | ||
| version = "0.1.2" | ||
| version = "0.1.3" | ||
| optional = true | ||
@@ -80,6 +80,11 @@ default-features = false | ||
| [dev-dependencies.bincode] | ||
| version = "1.3.3" | ||
| version = "1.3.1" | ||
| [dev-dependencies.postcard] | ||
| version = "1.0.1" | ||
| default-features = false | ||
| [dev-dependencies.serde] | ||
| version = "1.0.125" | ||
| version = "1.0.110" | ||
| default-features = false | ||
@@ -86,0 +91,0 @@ [features] |
+3
-1
@@ -5,3 +5,3 @@ UNICODE LICENSE V3 | ||
| Copyright © 2020-2023 Unicode, Inc. | ||
| Copyright © 2020-2024 Unicode, Inc. | ||
@@ -42,2 +42,4 @@ NOTICE TO USER: Carefully read the following legal agreement. BY | ||
| SPDX-License-Identifier: Unicode-3.0 | ||
| — | ||
@@ -44,0 +46,0 @@ |
+0
-1
@@ -26,3 +26,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// ``` | ||
| /// use std::borrow::Cow; | ||
| /// use std::rc::Rc; | ||
@@ -29,0 +28,0 @@ /// use yoke::either::EitherCart; |
+2
-1
@@ -27,3 +27,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| // https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations | ||
| // https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations | ||
| #![cfg_attr(all(not(test), not(doc)), no_std)] | ||
@@ -49,2 +49,3 @@ #![cfg_attr( | ||
| pub mod cartable_ptr; | ||
| pub mod either; | ||
@@ -51,0 +52,0 @@ #[cfg(feature = "alloc")] |
@@ -32,2 +32,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| //! ```compile_fail | ||
| //! # this compiles in 1.78+, so this text will make it fail | ||
| //! use yoke::*; | ||
@@ -34,0 +35,0 @@ //! |
+147
-14
@@ -5,2 +5,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::cartable_ptr::{CartableOptionPointer, CartablePointerLike}; | ||
| use crate::either::EitherCart; | ||
@@ -59,3 +60,3 @@ #[cfg(feature = "alloc")] | ||
| /// ```rust | ||
| /// # use yoke::{Yoke, Yokeable}; | ||
| /// # use yoke::Yoke; | ||
| /// # use std::rc::Rc; | ||
@@ -84,2 +85,7 @@ /// # use std::borrow::Cow; | ||
| yokeable: KindaSortaDangling<Y>, | ||
| // Safety invariant: this type can be anything, but `yokeable` may only contain references to | ||
| // StableDeref parts of this cart, and those references must be valid for the lifetime of | ||
| // this cart (it must own or borrow them). It's ok for this cart to contain stack data as long as it | ||
| // is not referenced by `yokeable` during construction. `attach_to_cart`, the typical constructor | ||
| // of this type, upholds this invariant, but other constructors like `replace_cart` need to uphold it. | ||
| cart: C, | ||
@@ -120,2 +126,5 @@ } | ||
| /// | ||
| /// The closure can read and write data outside of its scope, but data it returns | ||
| /// may borrow only from the argument passed to the closure. | ||
| /// | ||
| /// See also [`Yoke::try_attach_to_cart()`] to return a `Result` from the closure. | ||
@@ -129,3 +138,3 @@ /// | ||
| /// ``` | ||
| /// # use yoke::{Yoke, Yokeable}; | ||
| /// # use yoke::Yoke; | ||
| /// # use std::rc::Rc; | ||
@@ -150,2 +159,37 @@ /// # use std::borrow::Cow; | ||
| /// ``` | ||
| /// | ||
| /// Write the number of consumed bytes to a local variable: | ||
| /// | ||
| /// ``` | ||
| /// # use yoke::Yoke; | ||
| /// # use std::rc::Rc; | ||
| /// # use std::borrow::Cow; | ||
| /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> { | ||
| /// # // dummy implementation | ||
| /// # Rc::new([0x5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0, 0, 0]) | ||
| /// # } | ||
| /// | ||
| /// fn load_object( | ||
| /// filename: &str, | ||
| /// ) -> (Yoke<Cow<'static, str>, Rc<[u8]>>, usize) { | ||
| /// let rc: Rc<[u8]> = load_from_cache(filename); | ||
| /// let mut bytes_remaining = 0; | ||
| /// let bytes_remaining = &mut bytes_remaining; | ||
| /// let yoke = Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart( | ||
| /// rc, | ||
| /// |data: &[u8]| { | ||
| /// let mut d = postcard::Deserializer::from_bytes(data); | ||
| /// let output = serde::Deserialize::deserialize(&mut d); | ||
| /// *bytes_remaining = d.finalize().unwrap().len(); | ||
| /// Cow::Borrowed(output.unwrap()) | ||
| /// }, | ||
| /// ); | ||
| /// (yoke, *bytes_remaining) | ||
| /// } | ||
| /// | ||
| /// let (yoke, bytes_remaining) = load_object("filename.postcard"); | ||
| /// assert_eq!(&**yoke.get(), "hello"); | ||
| /// assert!(matches!(yoke.get(), &Cow::Borrowed(_))); | ||
| /// assert_eq!(bytes_remaining, 3); | ||
| /// ``` | ||
| pub fn attach_to_cart<F>(cart: C, f: F) -> Self | ||
@@ -219,3 +263,3 @@ where | ||
| /// ```rust | ||
| /// # use yoke::{Yoke, Yokeable}; | ||
| /// # use yoke::Yoke; | ||
| /// # use std::rc::Rc; | ||
@@ -314,2 +358,10 @@ /// # use std::borrow::Cow; | ||
| /// returned cart type `C`. | ||
| /// | ||
| /// For the purpose of determining this, `Yoke` guarantees that references from the Yokeable | ||
| /// `Y` into the cart `C` will never be references into its stack data, only heap data protected | ||
| /// by `StableDeref`. This does not necessarily mean that `C` implements `StableDeref`, rather that | ||
| /// any data referenced by `Y` must be accessed through a `StableDeref` impl on something `C` owns. | ||
| /// | ||
| /// Concretely, this means that if `C = Option<Rc<T>>`, `Y` may contain references to the `T` but not | ||
| /// anything else. | ||
| /// - Lifetimes inside C must not be lengthened, even if they are themselves contravariant. | ||
@@ -446,3 +498,2 @@ /// I.e., if C contains an `fn(&'a u8)`, it cannot be replaced with `fn(&'static u8), | ||
| /// # use std::borrow::Cow; | ||
| /// # use std::rc::Rc; | ||
| /// | ||
@@ -472,3 +523,5 @@ /// let owned: Cow<str> = "hello".to_owned().into(); | ||
| impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, Option<C>> { | ||
| // C does not need to be StableDeref here, if the yoke was constructed it's valid, | ||
| // and new_owned() doesn't construct a yokeable that uses references, | ||
| impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, Option<C>> { | ||
| /// Construct a new [`Yoke`] from static data. There will be no | ||
@@ -507,5 +560,7 @@ /// references to `cart` here since [`Yokeable`]s are `'static`, | ||
| /// | ||
| /// If the cart is `None`, this returns `Some`, but if the cart is `Some`, | ||
| /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`, | ||
| /// this returns `self` as an error. | ||
| pub fn try_into_yokeable(self) -> Result<Y, Self> { | ||
| // Safety: if the cart is None there is no way for the yokeable to | ||
| // have references into it because of the cart invariant. | ||
| match self.cart { | ||
@@ -518,2 +573,80 @@ Some(_) => Err(self), | ||
| impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, Option<C>> { | ||
| /// Converts a `Yoke<Y, Option<C>>` to `Yoke<Y, CartableOptionPointer<C>>` | ||
| /// for better niche optimization when stored as a field. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use std::borrow::Cow; | ||
| /// use yoke::Yoke; | ||
| /// | ||
| /// let yoke: Yoke<Cow<[u8]>, Box<Vec<u8>>> = | ||
| /// Yoke::attach_to_cart(vec![10, 20, 30].into(), |c| c.into()); | ||
| /// | ||
| /// let yoke_option = yoke.wrap_cart_in_option(); | ||
| /// let yoke_option_pointer = yoke_option.convert_cart_into_option_pointer(); | ||
| /// ``` | ||
| /// | ||
| /// The niche improves stack sizes: | ||
| /// | ||
| /// ``` | ||
| /// use yoke::Yoke; | ||
| /// use yoke::cartable_ptr::CartableOptionPointer; | ||
| /// use std::mem::size_of; | ||
| /// use std::rc::Rc; | ||
| /// | ||
| /// // The data struct is 6 words: | ||
| /// # #[derive(yoke::Yokeable)] | ||
| /// # struct MyDataStruct<'a> { | ||
| /// # _s: (usize, usize, usize, usize), | ||
| /// # _p: &'a str, | ||
| /// # } | ||
| /// const W: usize = core::mem::size_of::<usize>(); | ||
| /// assert_eq!(W * 6, size_of::<MyDataStruct>()); | ||
| /// | ||
| /// // An enum containing the data struct with an `Option<Rc>` cart is 8 words: | ||
| /// enum StaticOrYoke1 { | ||
| /// Static(&'static MyDataStruct<'static>), | ||
| /// Yoke(Yoke<MyDataStruct<'static>, Option<Rc<String>>>), | ||
| /// } | ||
| /// assert_eq!(W * 8, size_of::<StaticOrYoke1>()); | ||
| /// | ||
| /// // When using `CartableOptionPointer``, we need only 7 words for the same behavior: | ||
| /// enum StaticOrYoke2 { | ||
| /// Static(&'static MyDataStruct<'static>), | ||
| /// Yoke(Yoke<MyDataStruct<'static>, CartableOptionPointer<Rc<String>>>), | ||
| /// } | ||
| /// assert_eq!(W * 7, size_of::<StaticOrYoke2>()); | ||
| /// ``` | ||
| #[inline] | ||
| pub fn convert_cart_into_option_pointer(self) -> Yoke<Y, CartableOptionPointer<C>> { | ||
| match self.cart { | ||
| Some(cart) => Yoke { | ||
| yokeable: self.yokeable, | ||
| cart: CartableOptionPointer::from_cartable(cart), | ||
| }, | ||
| None => Yoke { | ||
| yokeable: self.yokeable, | ||
| cart: CartableOptionPointer::none(), | ||
| }, | ||
| } | ||
| } | ||
| } | ||
| impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, CartableOptionPointer<C>> { | ||
| /// Obtain the yokeable out of a `Yoke<Y, CartableOptionPointer<C>>` if possible. | ||
| /// | ||
| /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`, | ||
| /// this returns `self` as an error. | ||
| #[inline] | ||
| pub fn try_into_yokeable(self) -> Result<Y, Self> { | ||
| if self.cart.is_none() { | ||
| Ok(self.yokeable.into_inner()) | ||
| } else { | ||
| Err(self) | ||
| } | ||
| } | ||
| } | ||
| /// This trait marks cart types that do not change source on cloning | ||
@@ -528,6 +661,8 @@ /// | ||
| /// # Safety | ||
| /// This trait is safe to implement `StableDeref` types which, once `Clone`d, point to the same underlying data. | ||
| /// This trait is safe to implement on `StableDeref` types which, once `Clone`d, point to the same underlying data and retain ownership. | ||
| /// | ||
| /// (This trait is also implemented on `Option<T>` and `()`, which are the two non-`StableDeref` cart types that | ||
| /// Yokes can be constructed for) | ||
| /// This trait can also be implemented on aggregates of such types like `Option<T: CloneableCart>` and `(T: CloneableCart, U: CloneableCart)`. | ||
| /// | ||
| /// Essentially, all data that could be referenced by a Yokeable (i.e. data that is referenced via a StableDeref) must retain the same | ||
| /// pointer and ownership semantics once cloned. | ||
| pub unsafe trait CloneableCart: Clone {} | ||
@@ -622,3 +757,2 @@ | ||
| /// ``` | ||
| /// # use std::borrow::Cow; | ||
| /// # use yoke::{Yoke, Yokeable}; | ||
@@ -722,3 +856,2 @@ /// # use std::mem; | ||
| /// ``` | ||
| /// # use std::borrow::Cow; | ||
| /// # use yoke::{Yoke, Yokeable}; | ||
@@ -1299,3 +1432,3 @@ /// # use std::mem; | ||
| /// ```rust,compile_fail | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// use yoke::Yoke; | ||
| /// | ||
@@ -1312,3 +1445,3 @@ /// let cart = vec![1, 2, 3, 4].into_boxed_slice(); | ||
| /// ```rust | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// use yoke::Yoke; | ||
| /// | ||
@@ -1325,3 +1458,3 @@ /// let cart = vec![1, 2, 3, 4].into_boxed_slice(); | ||
| /// ```rust,compile_fail | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// use yoke::Yoke; | ||
| /// // longer lived | ||
@@ -1328,0 +1461,0 @@ /// let local = vec![4, 5, 6, 7]; |
+1
-1
@@ -58,3 +58,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| { | ||
| unsafe { f(mem::transmute(self)) } | ||
| unsafe { f(mem::transmute::<&mut Bar<'_>, &mut Bar<'a>>(self)) } | ||
| } | ||
@@ -61,0 +61,0 @@ } |
Sorry, the diff of this file is not supported yet