shapely_core/
slot.rs

1use std::collections::HashMap;
2use std::ptr::NonNull;
3
4use crate::{trace, InitMark, ShapeDesc, Shapely};
5
6/// Where to write the value
7enum Destination<'s> {
8    /// Writes directly to some address. If it's already initialized,
9    /// the old value is dropped in place.
10    ///
11    /// If the shape is a ZST, ptr will be dangling.
12    Ptr {
13        ptr: NonNull<u8>,
14        init_mark: InitMark<'s>,
15    },
16
17    /// Inserts into a HashMap<String, V>
18    HashMap { map: NonNull<u8>, key: String },
19}
20
21/// Allows writing into a struct field or inserting into a hash map.
22pub struct Slot<'s> {
23    /// where to write the value
24    dest: Destination<'s>,
25
26    /// shape of the field we're writing — used for validation
27    shape: ShapeDesc,
28}
29
30impl<'s> Slot<'s> {
31    /// Create a new slot for writing into a struct field — not just `foo.bar`, but also
32    /// `foo.2` for tuples, `foo.0` for newtype wrappers, etc.
33    #[inline(always)]
34    pub fn for_ptr(ptr: NonNull<u8>, shape: ShapeDesc, init_mark: InitMark<'s>) -> Self {
35        Self {
36            dest: Destination::Ptr { ptr, init_mark },
37            shape,
38        }
39    }
40
41    /// Create a new slot for writing into a HashMap. This is a different kind of slot because
42    /// the field _has_ to be allocated on the heap first and _then_ inserted into the hashmap.
43    #[inline(always)]
44    pub fn for_hash_map(map: NonNull<u8>, key: String, shape: ShapeDesc) -> Self {
45        Self {
46            dest: Destination::HashMap { map, key },
47            shape,
48        }
49    }
50
51    /// Fills the slot with a value of a concrete type. This performs a type check and panics if the
52    /// type is incompatible with the slot's shape.
53    ///
54    /// If the slot is already initialized, the old value is dropped.
55    pub fn fill<T: Shapely>(self, value: T) {
56        // should we provide fill_unchecked?
57        if self.shape != T::shape_desc() {
58            panic!(
59                "Attempted to fill a field with an incompatible shape.\n\
60                Expected shape: \x1b[33m{:?}\x1b[0m\n\
61                Actual shape: \x1b[33m{:?}\x1b[0m\n\
62                This is undefined behavior and we're refusing to proceed.",
63                self.shape.get(),
64                T::shape()
65            );
66        }
67        trace!(
68            "Filling slot with value of type: \x1b[33m{}\x1b[0m",
69            std::any::type_name::<T>()
70        );
71
72        match self.dest {
73            Destination::Ptr { ptr, mut init_mark } => {
74                if init_mark.get() {
75                    trace!("Field already initialized, dropping existing value");
76                    if let Some(drop_fn) = self.shape.get().drop_in_place {
77                        // Safety: The `drop_fn` is guaranteed to be a valid function pointer
78                        // for dropping the value at `ptr`. We've already checked that the
79                        // shape matches, and we're only calling this if the field is initialized.
80                        // The `ptr` is valid because it's part of the `Destination::Ptr` variant.
81                        unsafe {
82                            drop_fn(ptr.as_ptr());
83                        }
84                    }
85                }
86
87                trace!("Filling struct field at address: \x1b[33m{:?}\x1b[0m", ptr);
88                unsafe { std::ptr::write(ptr.as_ptr() as *mut T, value) };
89                init_mark.set();
90            }
91            Destination::HashMap { map, key } => {
92                let map = unsafe { &mut *(map.as_ptr() as *mut HashMap<String, T>) };
93                trace!("Inserting value into HashMap with key: \x1b[33m{key}\x1b[0m");
94                map.insert(key, value);
95            }
96        }
97    }
98
99    pub fn fill_from_partial(self, partial: crate::Partial<'_>) {
100        if self.shape != partial.shape() {
101            panic!(
102                "Attempted to fill a field with an incompatible shape.\n\
103                Expected shape: {:?}\n\
104                Actual shape: {:?}\n\
105                This is undefined behavior and we're refusing to proceed.",
106                self.shape.get(),
107                partial.shape().get()
108            );
109        }
110
111        unsafe {
112            match self.dest {
113                Destination::Ptr { ptr, mut init_mark } => {
114                    if init_mark.get() {
115                        if let Some(drop_fn) = self.shape.get().drop_in_place {
116                            drop_fn(ptr.as_ptr());
117                        }
118                    }
119                    partial.move_into(ptr);
120                    init_mark.set();
121                }
122                Destination::HashMap { map: _, ref key } => {
123                    trace!(
124                        "Filling HashMap entry: key=\x1b[33m{}\x1b[0m, src=\x1b[33m{:?}\x1b[0m, size=\x1b[33m{}\x1b[0m bytes",
125                        key,
126                        partial.addr.as_ptr(),
127                        self.shape.get().layout.size()
128                    );
129                    // TODO: Implement for HashMap
130                    // I guess we need another field in the vtable?
131                    panic!("fill_from_partial not implemented for HashMap");
132                }
133            }
134        }
135    }
136
137    pub fn shape(&self) -> ShapeDesc {
138        self.shape
139    }
140}