forked from unicode-org/icu4x
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathyoke.rs
139 lines (128 loc) · 4.37 KB
/
yoke.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// 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;
use crate::Cartable;
use crate::Yokeable;
use std::rc::Rc;
use std::sync::Arc;
/// A Cow-like borrowed object "yoked" to its backing data.
///
/// This allows things like zero copy deserialized data to carry around
/// shared references to their backing buffer.
///
/// `Y` (the [`Yokeable`]) is the object containing the references,
/// and will typically be of the form `Foo<'static>`. The `'static` is
/// not the actual lifetime of the data, rather it is a convenient way to erase
/// the lifetime and make it dynamic.
///
/// `C` is the "cart", which `Y` may contain references to. A [`Yoke`] can be constructed
/// with such references using [`Yoke::attach_to_cart()`].
///
/// # Example
///
/// For example, we can use this to store zero-copy deserialized data in a cache:
///
/// TODO this test is broken!
/// ```rust,ignore
/// # use yoke::{Yoke, Yokeable};
/// # use std::rc::Rc;
/// # use std::borrow::Cow;
/// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
/// # // dummy implementation
/// # Rc::new([0x68, 0x65, 0x6c, 0x6c, 0x6f])
/// # }
///
/// fn load_object(filename: &str) -> Yoke<Cow<'static, [u8]>, Rc<[u8]>> {
/// let rc: Rc<[u8]> = load_from_cache(filename);
/// Yoke::<Cow<'static, [u8]>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
/// bincode::deserialize_from(data).unwrap();
/// });
/// }
/// ```
///
pub struct Yoke<Y: for<'a> 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<Y: for<'a> Yokeable<'a>, C: Cart> Yoke<Y, C> {
/// Construct a new [`Yoke`] from static data. There will be no
/// references to `cart` here, this is good for e.g. constructing fully owned
/// [`Yoke`]s with no internal borrowing.
pub fn new(cart: C, yokeable: Y) -> Self {
Self { yokeable, cart }
}
/// Obtain a valid reference to the yokeable data
///
/// This essentially transforms the lifetime of the internal yokeable data to
/// be valid.
/// For example, if you're working with a `Yoke<Cow<'static, T>, C>`, this
/// will return an `&'a Cow<'a, T>`
pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
self.yokeable.transform()
}
/// Get a reference to the backing cart.
pub fn backing_cart(&self) -> &C {
&self.cart
}
pub fn with_mut<'a, F>(&'a mut self, f: F)
where
F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output),
{
self.yokeable.with_mut(f)
}
pub fn attach_to_cart<F>(cart: C, f: F) -> Self
where
F: for<'de> FnOnce(&'de C::Inner) -> (<Y as Yokeable<'de>>::Output),
{
let deserialized = f(cart.get_inner());
Self {
yokeable: unsafe { Y::make(deserialized) },
cart,
}
}
}
// clone impls only work for reference counted objects, otherwise you should be
// cloning `backing_cart()` and reusing `attach_to_cart()`
impl<Y: for<'a> Yokeable<'a>, T: Cartable + ?Sized> Clone for Yoke<Y, Rc<T>>
where
for<'a> <Y as Yokeable<'a>>::Output: Clone,
{
fn clone(&self) -> Self {
Yoke {
yokeable: unsafe { Y::make(self.get().clone()) },
cart: self.cart.clone(),
}
}
}
impl<Y: for<'a> Yokeable<'a>, T: Cartable + ?Sized> Clone for Yoke<Y, Arc<T>>
where
for<'a> <Y as Yokeable<'a>>::Output: Clone,
{
fn clone(&self) -> Self {
Yoke {
yokeable: unsafe { Y::make(self.get().clone()) },
cart: self.cart.clone(),
}
}
}
#[test]
fn this_test_is_broken() {
use crate::{Yoke, Yokeable};
use std::borrow::Cow;
use std::rc::Rc;
fn load_from_cache(_filename: &str) -> Rc<[u8]> {
// dummy implementation
Rc::new([0x68, 0x65, 0x6c, 0x6c, 0x6f])
}
fn load_object(filename: &str) -> Yoke<Cow<'static, [u8]>, Rc<[u8]>> {
let rc: Rc<[u8]> = load_from_cache(filename);
Yoke::<Cow<'static, [u8]>, Rc<[u8]>>::attach_to_cart(rc, deserialize);
unimplemented!()
}
fn deserialize<'d>(data: &'d [u8]) -> <Cow<'static, [u8]> as Yokeable<'d>>::Output {
bincode::deserialize_from(data).unwrap()
}
}