|
1 | 1 |
|
2 | | -use std::mem::transmute; |
3 | 2 | use std::marker::PhantomData; |
4 | 3 | use std::ops::{Deref, DerefMut}; |
5 | 4 |
|
6 | 5 | use super::{Abomonation, decode}; |
7 | 6 |
|
8 | 7 | /// A type wrapping owned decoded abomonated data. |
9 | 8 | /// |
10 | | -/// This type ensures that decoding and pointer correction has already happened, |
11 | | -/// and implements `Deref<Target=T>` using a pointer cast (transmute). |
| 9 | +/// This type ensures that decoding and pointer correction has already happened. |
| 10 | +/// It provides a way to move the deserialized data around, while keeping |
| 11 | +/// on-demand access to it via the `as_ref()` method. |
| 12 | +/// |
| 13 | +/// As an extra convenience, `Deref<Target=T>` is also implemented if T does |
| 14 | +/// not contain references. Unfortunately, this trait cannot be safely |
| 15 | +/// implemented when T does contain references. |
12 | 16 | /// |
13 | 17 | /// #Safety |
14 | 18 | /// |
15 | | -/// The safety of this type, and in particular its `transute` implementation of |
16 | | -/// the `Deref` trait, relies on the owned bytes not being externally mutated |
17 | | -/// once provided. You could imagine a new type implementing `DerefMut` as required, |
18 | | -/// but which also retains the ability (e.g. through `RefCell`) to mutate the bytes. |
19 | | -/// This would be very bad, but seems hard to prevent in the type system. Please |
20 | | -/// don't do this. |
| 19 | +/// The safety of this type, and in particular of access to the deserialized |
| 20 | +/// data, relies on the owned bytes not being externally mutated after the |
| 21 | +/// `Abomonated` is constructed. You could imagine a new type implementing |
| 22 | +/// `DerefMut` as required, but which also retains the ability (e.g. through |
| 23 | +/// `RefCell`) to mutate the bytes. This would be very bad, but seems hard to |
| 24 | +/// prevent in the type system. Please don't do this. |
21 | 25 | /// |
22 | 26 | /// You must also use a type `S` whose bytes have a fixed location in memory. |
23 | 27 | /// Otherwise moving an instance of `Abomonated<T, S>` may invalidate decoded |
@@ -54,9 +58,10 @@ pub struct Abomonated<T, S: DerefMut<Target=[u8]>> { |
54 | 58 | decoded: S, |
55 | 59 | } |
56 | 60 |
|
57 | | -impl<'bytes, T, S> Abomonated<T, S> |
58 | | - where S: DerefMut<Target=[u8]> + 'bytes, |
59 | | - T: Abomonation<'bytes>, |
| 61 | +impl<'s, 't, T, S> Abomonated<T, S> |
| 62 | + where S: DerefMut<Target=[u8]> + 's, |
| 63 | + T: Abomonation<'t>, |
| 64 | + 's: 't |
60 | 65 | { |
61 | 66 | /// Attempts to create decoded data from owned mutable bytes. |
62 | 67 | /// |
@@ -95,59 +100,65 @@ impl<'bytes, T, S> Abomonated<T, S> |
95 | 100 | /// The type `S` must have its bytes at a fixed location, which will |
96 | 101 | /// not change if the `bytes: S` instance is moved. Good examples are |
97 | 102 | /// `Vec<u8>` whereas bad examples are `[u8; 16]`. |
98 | | - pub unsafe fn new(bytes: S) -> Option<Self> { |
99 | | - // FIXME: Our API specification for `T` and `S` interacts with the API |
100 | | - // specification of `decode()` in such an unfortunate way that |
101 | | - // `decode::<T>(bytes.borrow_mut())` borrows `bytes` forever. |
| 103 | + pub unsafe fn new(mut bytes: S) -> Option<Self> { |
| 104 | + // Fix type `T`'s inner pointers. Will return `None` on failure. |
102 | 105 | // |
103 | | - // This is not what we want here, because it prevents moving |
104 | | - // `bytes` into the `Abomonated` at the end of the function. |
| 106 | + // FIXME: `slice::from_raw_parts_mut` is used to work around the borrow |
| 107 | + // checker marking `bytes` as forever borrowed if `&mut bytes` is |
| 108 | + // directly passed as input to `decode()`. But that is itself a |
| 109 | + // byproduct of the API contract specified by the `where` clause |
| 110 | + // above, which allows S to be `&'t mut [u8]` (and therefore |
| 111 | + // require such a perpetual borrow) in the worst case. |
105 | 112 | // |
106 | | - // I could not find a way to redesign the API of either |
107 | | - // `Abomonated` or `decode()` so that this doesn't happen. |
108 | | - // Therefore, I will have to work around it the unsafe way... |
| 113 | + // A different API contract might allow us to achieve the same |
| 114 | + // result without resorting to such evil unsafe tricks. |
109 | 115 | // |
110 | | - let bytes_uc = std::cell::UnsafeCell::new(bytes); |
111 | | - |
112 | | - { |
113 | | - // Create an `&'bytes mut [u8]` slice from `bytes_uc`, without |
114 | | - // borrowing `bytes_uc` in the process (`get()` returns `*mut _`) |
115 | | - // |
116 | | - // This is safe because `UnsafeCell<T>` can alias with `&mut T`. |
117 | | - // |
118 | | - let bytes: &'bytes mut [u8] = (*bytes_uc.get()).deref_mut(); |
| 116 | + decode::<T>(std::slice::from_raw_parts_mut(bytes.as_mut_ptr(), |
| 117 | + bytes.len()))?; |
119 | 118 |
|
120 | | - // Fix type `T`'s inner pointers (will exit early on failure) |
121 | | - // |
122 | | - // This borrows the `bytes` slice for its entire `'bytes` lifetime! |
123 | | - // But it's okay, we'll just throw that slice away at the end of the |
124 | | - // scope, without letting any reference to `bytes_uc` leak. |
125 | | - // |
126 | | - // As we have not borrowed `bytes_uc`, we can then keep using it. |
127 | | - // |
128 | | - decode::<T>(bytes)?; |
129 | | - } |
130 | | - |
131 | | - // Get back the original `bytes` and return the Abomonated structure |
| 119 | + // Build the Abomonated structure |
132 | 120 | Some(Abomonated { |
133 | 121 | phantom: PhantomData, |
134 | | - decoded: bytes_uc.into_inner(), |
| 122 | + decoded: bytes, |
135 | 123 | }) |
136 | 124 | } |
137 | 125 | } |
138 | 126 |
|
139 | | -impl<T, S: DerefMut<Target=[u8]>> Abomonated<T, S> { |
| 127 | +impl<'t, T, S> Abomonated<T, S> |
| 128 | + where S: DerefMut<Target=[u8]>, |
| 129 | + T: Abomonation<'t> |
| 130 | +{ |
| 131 | + /// Get a read-only view on the deserialized bytes |
140 | 132 | pub fn as_bytes(&self) -> &[u8] { |
141 | 133 | &self.decoded |
142 | 134 | } |
143 | | -} |
144 | 135 |
|
| 136 | + /// Get a read-only view on the deserialized data |
| 137 | + // |
| 138 | + // NOTE: This method can be safely used even if type T contains references, |
| 139 | + // because it makes sure that the borrow of `self` lasts long enough |
| 140 | + // to encompass the lifetime of these references. |
| 141 | + // |
| 142 | + // Otherwise, it would be possible to extract an `&'static Something` |
| 143 | + // from a short-lived borrow of a `Box<[u8]>`, then drop the `Box`, |
| 144 | + // and end up with a dangling reference. |
| 145 | + // |
| 146 | + pub fn as_ref<'a: 't>(&'a self) -> &'a T { |
| 147 | + unsafe { &*(self.decoded.as_ptr() as *const T) } |
| 148 | + } |
| 149 | +} |
145 | 150 |
|
146 | | -impl<T, S: DerefMut<Target=[u8]>> Deref for Abomonated<T, S> { |
| 151 | +// NOTE: The lifetime constraint that was applied to `as_ref()` cannot be |
| 152 | +// applied to a `Deref` implementation. Therefore, `Deref` can only be |
| 153 | +// used on types T which do not contain references, as enforced by the |
| 154 | +// higher-ranked trait bound below. |
| 155 | +impl<T, S> Deref for Abomonated<T, S> |
| 156 | + where S: DerefMut<Target=[u8]>, |
| 157 | + for<'t> T: Abomonation<'t>, |
| 158 | +{ |
147 | 159 | type Target = T; |
148 | 160 | #[inline] |
149 | 161 | fn deref(&self) -> &T { |
150 | | - let result: &T = unsafe { transmute(self.decoded.get_unchecked(0)) }; |
151 | | - result |
| 162 | + self.as_ref() |
152 | 163 | } |
153 | 164 | } |
0 commit comments