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