diff --git a/Cargo.lock b/Cargo.lock index 2a00eef0141..a31e1832c7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2441,6 +2441,10 @@ dependencies = [ "icu_benchmark_macros", ] +[[package]] +name = "yoke" +version = "0.1.0" + [[package]] name = "zerovec" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 539f4985679..50bad3f890d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ members = [ "utils/litemap", "utils/pattern", "utils/writeable", + "utils/yoke", "utils/zerovec", ] diff --git a/utils/yoke/Cargo.toml b/utils/yoke/Cargo.toml new file mode 100644 index 00000000000..9e5c6cd1ea6 --- /dev/null +++ b/utils/yoke/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "yoke" +version = "0.1.0" +authors = ["Manish Goregaokar "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/utils/yoke/src/cart.rs b/utils/yoke/src/cart.rs new file mode 100644 index 00000000000..aa02583768b --- /dev/null +++ b/utils/yoke/src/cart.rs @@ -0,0 +1,15 @@ +// 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 ). + +use std::rc::Rc; + +pub trait Cart { + fn as_bytes(&self) -> &[u8]; +} + +impl Cart for Rc<[u8]> { + fn as_bytes(&self) -> &[u8] { + &self + } +} diff --git a/utils/yoke/src/lib.rs b/utils/yoke/src/lib.rs new file mode 100644 index 00000000000..72e590280c7 --- /dev/null +++ b/utils/yoke/src/lib.rs @@ -0,0 +1,11 @@ +// 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 ). + +mod cart; +mod yoke; +mod yokeable; + +pub use cart::Cart; +pub use yoke::Yoke; +pub use yokeable::Yokeable; diff --git a/utils/yoke/src/yoke.rs b/utils/yoke/src/yoke.rs new file mode 100644 index 00000000000..99496569d81 --- /dev/null +++ b/utils/yoke/src/yoke.rs @@ -0,0 +1,43 @@ +// 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 ). + +use crate::{Cart, Yokeable}; + +pub struct Yoke Yokeable<'a>, C: Cart> { + // must be the first field for drop order + // this will have a 'static lifetime parameter, that parameter is a lie + yokeable: Y, + cart: C, +} + +impl Yokeable<'a>, C: Cart> Yoke { + pub fn new(cart: C, yokeable: Y) -> Self { + Self { yokeable, cart } + } + pub fn get<'a>(&'a self) -> &'a >::Output { + self.yokeable.transform() + } + + pub fn backing_cart(&self) -> &C { + &self.cart + } + pub fn attach_to_cart(cart: C, f: F) -> Self + where + for<'de> F: FnOnce(&'de [u8]) -> >::Output, + { + let bytes = cart.as_bytes(); + let deserialized = f(bytes); + Self { + yokeable: unsafe { Y::make(deserialized) }, + cart, + } + } + + pub fn with_mut<'a, F>(&'a mut self, f: F) + where + F: 'static + FnOnce(&'a mut >::Output), + { + self.yokeable.with_mut(f) + } +} diff --git a/utils/yoke/src/yokeable.rs b/utils/yoke/src/yokeable.rs new file mode 100644 index 00000000000..d564e9e0168 --- /dev/null +++ b/utils/yoke/src/yokeable.rs @@ -0,0 +1,58 @@ +// 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 ). + +use std::borrow::{Cow, ToOwned}; +use std::mem; + +pub unsafe trait Yokeable<'a>: 'static { + type Output: 'a + Sized; // MUST be `Self` with the lifetime swapped + // used by `SharedData::get()` + fn transform(&'a self) -> &'a Self::Output; + + // Used for zero-copy deserialization. Safety constraint: `Self` must be + // destroyed before the data `Self::Output` was deserialized + // from is + unsafe fn make(from: Self::Output) -> Self; + + fn with_mut(&'a mut self, f: F) + where + F: 'static + FnOnce(&'a mut Self::Output); +} + +unsafe impl<'a, T: 'static + ToOwned + ?Sized> Yokeable<'a> for Cow<'static, T> +where + ::Owned: Sized, +{ + type Output = Cow<'a, T>; + fn transform(&'a self) -> &'a Cow<'a, T> { + self + } + + unsafe fn make(from: Cow<'a, T>) -> Self { + debug_assert!(mem::size_of::>() == mem::size_of::()); + // i hate this + // unfortunately Rust doesn't think `mem::transmute` is possible since it's not sure the sizes + // are the same + mem::transmute_copy(&from) + } + + fn with_mut(&'a mut self, f: F) + where + F: 'static + FnOnce(&'a mut Self::Output), + { + unsafe { f(mem::transmute(self)) } + } +} + +// The following code should NOT compile!!! +// +// struct Foo { +// str: String, +// cow: Cow<'static, str> +// } + +// fn unsound<'a>(foo: &'a mut Foo) { +// let a: &str = &foo.str; +// foo.cow.with_mut(|cow| *cow = Cow::Borrowed(a)); +// }