twizzler/object/
builder.rs

1use std::{ffi::c_char, marker::PhantomData, mem::MaybeUninit};
2
3use twizzler_rt_abi::{
4    bindings::{object_source, object_tie},
5    error::RawTwzError,
6    object::{
7        BackingType, CreateTieSpec, LifetimeType, MapFlags, ObjectCreate, ObjectCreateFlags,
8        ObjectSource, Protections,
9    },
10};
11
12use super::{Object, TxObject};
13use crate::{
14    marker::{BaseType, StoreCopy},
15    Result,
16};
17
18/// An object builder, for constructing objects using a builder API.
19#[derive(Clone)]
20pub struct ObjectBuilder<Base: BaseType> {
21    spec: ObjectCreate,
22    src_objs: Vec<object_source>,
23    ties: Vec<object_tie>,
24    name: Option<String>,
25    _pd: PhantomData<Base>,
26}
27
28impl<Base: BaseType> ObjectBuilder<Base> {
29    /// Make a new object builder.
30    pub fn new(spec: ObjectCreate) -> Self {
31        Self {
32            spec,
33            _pd: PhantomData,
34            name: None,
35            src_objs: Vec::new(),
36            ties: Vec::new(),
37        }
38    }
39
40    /// Make the object persistent.
41    pub fn persist(mut self, persist: bool) -> Self {
42        if persist {
43            self.spec.lt = LifetimeType::Persistent;
44        } else {
45            self.spec.lt = LifetimeType::Volatile;
46        }
47        self
48    }
49
50    /// Cast the base type.
51    pub fn cast<U: BaseType>(self) -> ObjectBuilder<U> {
52        ObjectBuilder::<U>::new(self.spec)
53    }
54
55    /// Add a Source Object that this new object will copy from.
56    pub fn add_src(mut self, obj_src: ObjectSource) -> Self {
57        self.src_objs.push(obj_src.into());
58        self
59    }
60
61    /// Add a tie specification for this object creation.
62    pub fn add_tie(mut self, tie: CreateTieSpec) -> Self {
63        self.ties.push(tie.into());
64        self
65    }
66
67    pub fn named(mut self, name: impl ToString) -> Self {
68        self.name = Some(name.to_string());
69        self
70    }
71}
72
73impl<Base: BaseType + StoreCopy> ObjectBuilder<Base> {
74    /// Build an object using the provided base vale.
75    /// # Example
76    /// ```
77    /// # use twizzler::object::ObjectBuilder;
78    /// let builder = ObjectBuilder::default();
79    /// let obj = builder.build(42u32).unwrap();
80    /// ```
81    pub fn build(&self, base: Base) -> Result<Object<Base>> {
82        self.build_inplace(|tx| tx.write(base))
83    }
84}
85
86impl<Base: BaseType> ObjectBuilder<Base> {
87    /// Build an object using the provided constructor function.
88    ///
89    /// The constructor should call the .write() method on the TxObject, and
90    /// return the result.
91    /// # Example
92    /// ```
93    /// # use twizzler::object::ObjectBuilder;
94    /// let builder = ObjectBuilder::default();
95    /// let obj = builder.build_inplace(|tx| tx.write(42u32)).unwrap();
96    /// ```
97    pub fn build_inplace<F>(&self, ctor: F) -> Result<Object<Base>>
98    where
99        F: FnOnce(TxObject<MaybeUninit<Base>>) -> Result<TxObject<Base>>,
100    {
101        let id = unsafe {
102            twizzler_rt_abi::bindings::twz_rt_create_object(
103                &self.spec.into(),
104                self.src_objs.as_slice().as_ptr(),
105                self.src_objs.len(),
106                self.ties.as_slice().as_ptr(),
107                self.ties.len(),
108                self.name
109                    .as_ref()
110                    .map(|s| s.as_ptr().cast::<c_char>())
111                    .unwrap_or(core::ptr::null())
112                    .cast(),
113                self.name.as_ref().map(|s| s.len()).unwrap_or(0),
114            )
115        };
116        if id.err != 0 {
117            return Err(RawTwzError::new(id.err).error());
118        }
119        let id = id.val.into();
120        let mut flags = MapFlags::READ | MapFlags::WRITE;
121        if self.spec.lt == LifetimeType::Persistent {
122            flags.insert(MapFlags::PERSIST);
123        } else {
124            if let Some(ref name) = self.name {
125                tracing::warn!(
126                    "tried to name volatile object at creation time: {} {}",
127                    id,
128                    name
129                );
130            }
131        }
132        let mu_object = unsafe { Object::<MaybeUninit<Base>>::map_unchecked(id, flags) }?;
133        let object = ctor(mu_object.into_tx()?)?;
134        object.into_object()
135    }
136
137    /// Build an object using the provided constructor function.
138    ///
139    /// The constructor should call the .write() method on the TxObject or
140    /// otherwise ensure that it is safe to call .assume_init on the underlying
141    /// MaybeUninit.
142    ///
143    /// # Safety
144    /// The caller must ensure that the base is initialized, see MaybeUninit::assume_init.
145    ///
146    /// # Example
147    /// ```
148    /// # use twizzler::object::ObjectBuilder;
149    /// let builder = ObjectBuilder::default();
150    /// let obj = unsafe {
151    ///     builder
152    ///         .build_ctor(|tx| {
153    ///             tx.write(42u32);
154    ///         })
155    ///         .unwrap()
156    /// };
157    /// ```
158    pub unsafe fn build_ctor<F>(&self, ctor: F) -> Result<Object<Base>>
159    where
160        F: FnOnce(&mut TxObject<MaybeUninit<Base>>),
161    {
162        self.build_inplace(|mut tx| {
163            ctor(&mut tx);
164            Ok(tx.assume_init())
165        })
166    }
167}
168
169impl<Base: BaseType> Default for ObjectBuilder<Base> {
170    fn default() -> Self {
171        Self::new(ObjectCreate::new(
172            BackingType::Normal,
173            LifetimeType::Volatile,
174            None,
175            ObjectCreateFlags::empty(),
176            Protections::all(),
177        ))
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::ObjectBuilder;
184    use crate::{marker::BaseType, object::TypedObject, ptr::InvPtr};
185
186    #[test]
187    fn builder_simple() {
188        let builder = ObjectBuilder::default();
189        let obj = builder.build(42u32).unwrap();
190        let base = obj.base();
191        assert_eq!(*base, 42);
192    }
193
194    struct Foo {
195        ptr: InvPtr<u32>,
196    }
197    impl BaseType for Foo {}
198
199    #[test]
200    fn builder_complex() {
201        let builder = ObjectBuilder::default();
202        let obj_1 = builder.build(42u32).unwrap();
203        let base = obj_1.base_ref();
204        assert_eq!(*base, 42);
205
206        let builder = ObjectBuilder::<Foo>::default();
207        let obj = builder
208            .build_inplace(|tx| {
209                let foo = Foo {
210                    ptr: InvPtr::new(&tx, base)?,
211                };
212                tx.write(foo)
213            })
214            .unwrap();
215        let base_foo = obj.base();
216        let r = unsafe { base_foo.ptr.resolve() };
217        assert_eq!(*r, 42);
218    }
219}