roam_frame/
owned_message.rs

1// SAFETY: this module uses unsafe for a self-referential wrapper (yoke-like).
2#![allow(unsafe_code)]
3
4use std::mem::ManuallyDrop;
5
6use crate::Frame;
7
8/// A deserialized value co-located with its backing frame.
9///
10/// The value can borrow from the frame's payload bytes, enabling zero-copy
11/// deserialization for `&'a [u8]`, `&'a str`, and `Cow<'a, _>`.
12///
13/// `T` must be covariant in any lifetime parameters; this is checked at runtime
14/// using facet's variance tracking.
15pub struct OwnedMessage<T: 'static> {
16    value: ManuallyDrop<T>,
17    frame: ManuallyDrop<Box<Frame>>,
18}
19
20impl<T: 'static> Drop for OwnedMessage<T> {
21    fn drop(&mut self) {
22        // Drop value first (it may borrow from frame), then frame.
23        unsafe {
24            ManuallyDrop::drop(&mut self.value);
25            ManuallyDrop::drop(&mut self.frame);
26        }
27    }
28}
29
30impl<T: 'static + facet::Facet<'static>> OwnedMessage<T> {
31    #[inline]
32    pub fn try_new<E>(
33        frame: Frame,
34        builder: impl FnOnce(&'static [u8]) -> Result<T, E>,
35    ) -> Result<Self, E> {
36        let variance = T::SHAPE.computed_variance();
37        assert!(
38            variance.can_shrink(),
39            "OwnedMessage<T> requires T to be covariant (lifetime can shrink safely). Type {:?} has variance {:?}",
40            T::SHAPE.type_identifier,
41            variance
42        );
43
44        let frame = Box::new(frame);
45
46        // Create a 'static slice pointing to the boxed frame's payload bytes.
47        // This is sound because:
48        // - Inline payload lives inside the boxed descriptor (stable address)
49        // - Other payload variants live in stable heap storage
50        // - We drop `value` before `frame`
51        let payload: &'static [u8] = unsafe {
52            let bytes = (*frame).payload_bytes();
53            std::slice::from_raw_parts(bytes.as_ptr(), bytes.len())
54        };
55
56        let value = builder(payload)?;
57
58        Ok(Self {
59            value: ManuallyDrop::new(value),
60            frame: ManuallyDrop::new(frame),
61        })
62    }
63
64    #[inline]
65    pub fn new(frame: Frame, builder: impl FnOnce(&'static [u8]) -> T) -> Self {
66        Self::try_new(frame, |payload| {
67            Ok::<_, std::convert::Infallible>(builder(payload))
68        })
69        .unwrap_or_else(|e: std::convert::Infallible| match e {})
70    }
71}
72
73impl<T: 'static> OwnedMessage<T> {
74    #[inline]
75    pub fn frame(&self) -> &Frame {
76        &self.frame
77    }
78
79    #[inline]
80    pub fn value(&self) -> &T {
81        &self.value
82    }
83
84    #[inline]
85    pub fn into_frame(mut self) -> Frame {
86        unsafe {
87            ManuallyDrop::drop(&mut self.value);
88        }
89        let frame = unsafe { ManuallyDrop::take(&mut self.frame) };
90        std::mem::forget(self);
91        *frame
92    }
93}
94
95impl<T: 'static> std::ops::Deref for OwnedMessage<T> {
96    type Target = T;
97
98    #[inline]
99    fn deref(&self) -> &T {
100        &self.value
101    }
102}
103
104impl<T: 'static> AsRef<T> for OwnedMessage<T> {
105    #[inline]
106    fn as_ref(&self) -> &T {
107        &self.value
108    }
109}
110
111impl<T: 'static + std::fmt::Debug> std::fmt::Debug for OwnedMessage<T> {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.debug_struct("OwnedMessage")
114            .field("value", &*self.value)
115            .finish_non_exhaustive()
116    }
117}