rocket/form/
buffer.rs

1use std::ops::{Index, RangeFrom, RangeTo};
2use std::cell::UnsafeCell;
3
4use parking_lot::{RawMutex, lock_api::RawMutex as _};
5
6mod private {
7    /// Sealed trait for types that can be shared in a `SharedStack`.
8    ///
9    /// The type of values passed to
10    /// [`local_cache`](crate::request::local_cache) must implement this trait.
11    /// Since this trait is sealed, the types implementing this trait are known
12    /// and finite: `String` and `Vec<T> for all T: Sync + Send + 'static`.
13    // UNSAFE: Needs to have a stable address when deref'd.
14    pub unsafe trait Shareable: std::ops::Deref + Sync + Send + 'static {
15        /// The current length of the owned shareable.
16        fn len(&self) -> usize;
17    }
18
19    unsafe impl Shareable for String {
20        fn len(&self) -> usize { self.len() }
21    }
22
23    unsafe impl<T: Send + Sync + 'static> Shareable for Vec<T> {
24        fn len(&self) -> usize { self.len() }
25    }
26}
27
28pub use private::Shareable;
29
30/// A stack of strings (chars of bytes) that can be shared between threads while
31/// remaining internally mutable and while allowing references into the stack to
32/// persist across mutations.
33pub struct SharedStack<T: Shareable> {
34    stack: UnsafeCell<Vec<T>>,
35    mutex: RawMutex,
36}
37
38impl<T: Shareable> SharedStack<T>
39    where T::Target: Index<RangeFrom<usize>, Output = T::Target> +
40                     Index<RangeTo<usize>, Output = T::Target>
41{
42    /// Creates a new stack.
43    pub fn new() -> Self {
44        SharedStack {
45            stack: UnsafeCell::new(vec![]),
46            mutex: RawMutex::INIT,
47        }
48    }
49
50    /// Pushes the string `S` onto the stack. Returns a reference of the string
51    /// in the stack.
52    pub(crate) fn push<S: Into<T>>(&self, string: S) -> &T::Target {
53        // SAFETY:
54        //   * Aliasing: We retrieve a mutable reference to the last slot (via
55        //     `push()`) and then return said reference as immutable; these
56        //     occur in serial, so they don't alias. This method accesses a
57        //     unique slot each call: the last slot, subsequently replaced by
58        //     `push()` each next call. No other method accesses the internal
59        //     buffer directly. Thus, the outstanding reference to the last slot
60        //     is never accessed again mutably, preserving aliasing guarantees.
61        //   * Liveness: The returned reference is to a `String`; we must ensure
62        //     that the `String` is never dropped while `self` lives. This is
63        //     guaranteed by returning a reference with the same lifetime as
64        //     `self`, so `self` can't be dropped while the string is live, and
65        //     by never removing elements from the internal `Vec` thus not
66        //     dropping `String` itself: `push()` is the only mutating operation
67        //     called on `Vec`, which preserves all previous elements; the
68        //     stability of `String` itself means that the returned address
69        //     remains valid even after internal realloc of `Vec`.
70        //   * Thread-Safety: Parallel calls to `push_one` without exclusion
71        //     would result in a race to `vec.push()`; `RawMutex` ensures that
72        //     this doesn't occur.
73        unsafe {
74            self.mutex.lock();
75            let vec: &mut Vec<T> = &mut *self.stack.get();
76            vec.push(string.into());
77            let last = vec.last().expect("push() => non-empty");
78            self.mutex.unlock();
79            last
80        }
81    }
82
83    /// Just like `push` but `string` must already be the owned `T`.
84    pub fn push_owned(&self, string: T) -> &T::Target {
85        self.push(string)
86    }
87
88    /// Pushes the string `S` onto the stack which is assumed to internally
89    /// contain two strings with the first string being of length `n`. Returns
90    /// references to the two strings on the stack.
91    ///
92    /// # Panics
93    ///
94    /// Panics if `string.len() < len`.
95    pub(crate) fn push_split<S: Into<T>>(&self, string: S, n: usize) -> (&T::Target, &T::Target) {
96        let buffered = self.push(string);
97        let a = &buffered[..n];
98        let b = &buffered[n..];
99        (a, b)
100    }
101
102    /// Pushes the strings `a` and `b` onto the stack without allocating for
103    /// both strings. Returns references to the two strings on the stack.
104    pub(crate) fn push_two<'a, V>(&'a self, a: V, b: V) -> (&'a T::Target, &'a T::Target)
105        where T: From<V> + Extend<V>,
106    {
107        let mut value = T::from(a);
108        let split_len = value.len();
109        value.extend(Some(b));
110        self.push_split(value, split_len)
111    }
112}
113
114unsafe impl<T: Shareable> Sync for SharedStack<T> {}