twizzler/object/
builder.rs

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