facet_poke/
lib.rs

1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4//! Allows poking (writing to) shapes
5
6use core::alloc::Layout;
7
8pub use facet_peek::*;
9
10use facet_trait::{Def, Facet, OpaqueUninit, Shape};
11
12mod value;
13pub use value::*;
14
15mod list;
16pub use list::*;
17
18mod map;
19pub use map::*;
20
21mod struct_;
22pub use struct_::*;
23
24mod enum_;
25pub use enum_::*;
26
27/// Allows writing values of different kinds.
28#[non_exhaustive]
29pub enum Poke<'mem> {
30    /// A scalar value. See [`PokeValue`].
31    Scalar(PokeValue<'mem>),
32    /// A list (array/vec/etc). See [`PokeList`].
33    List(PokeListUninit<'mem>),
34    /// A map (HashMap/BTreeMap/etc). See [`PokeMap`].
35    Map(PokeMapUninit<'mem>),
36    /// A struct, tuple struct, or tuple. See [`PokeStruct`].
37    Struct(PokeStruct<'mem>),
38    /// An enum variant. See [`PokeEnum`].
39    Enum(PokeEnumNoVariant<'mem>),
40}
41
42/// Ensures a value is dropped when the guard is dropped.
43pub struct Guard {
44    ptr: *mut u8,
45    layout: Layout,
46    shape: &'static Shape,
47}
48
49impl Drop for Guard {
50    fn drop(&mut self) {
51        unsafe {
52            std::alloc::dealloc(self.ptr, self.layout);
53        }
54    }
55}
56
57impl<'mem> Poke<'mem> {
58    /// Allocates a new poke of a type that implements facet
59    pub fn alloc<S: Facet>() -> (Self, Guard) {
60        let data = S::SHAPE.allocate();
61        let layout = Layout::new::<S>();
62        let guard = Guard {
63            ptr: data.as_mut_ptr(),
64            layout,
65            shape: S::SHAPE,
66        };
67        let poke = unsafe { Self::unchecked_new(data, S::SHAPE) };
68        (poke, guard)
69    }
70
71    /// Allocates a new poke from a given shape
72    pub fn alloc_shape(shape: &'static Shape) -> (Self, Guard) {
73        let data = shape.allocate();
74        let layout = shape.layout;
75        let guard = Guard {
76            ptr: data.as_mut_ptr(),
77            layout,
78            shape,
79        };
80        let poke = unsafe { Self::unchecked_new(data, shape) };
81        (poke, guard)
82    }
83
84    /// Creates a new peek, for easy manipulation of some opaque data.
85    ///
86    /// # Safety
87    ///
88    /// `data` must be initialized and well-aligned, and point to a value
89    /// of the type described by `shape`.
90    pub unsafe fn unchecked_new(data: OpaqueUninit<'mem>, shape: &'static Shape) -> Self {
91        match shape.def {
92            Def::Struct(struct_def) => {
93                Poke::Struct(unsafe { PokeStruct::new(data, shape, struct_def) })
94            }
95            Def::Map(map_def) => {
96                let pmu = unsafe { PokeMapUninit::new(data, shape, map_def) };
97                Poke::Map(pmu)
98            }
99            Def::List(list_def) => {
100                let plu = unsafe { PokeListUninit::new(data, shape, list_def) };
101                Poke::List(plu)
102            }
103            Def::Scalar { .. } => Poke::Scalar(unsafe { PokeValue::new(data, shape) }),
104            Def::Enum(enum_def) => {
105                Poke::Enum(unsafe { PokeEnumNoVariant::new(data, shape, enum_def) })
106            }
107            _ => todo!("unsupported def: {:?}", shape.def),
108        }
109    }
110
111    /// Converts this Poke into a PokeStruct, panicking if it's not a Struct variant
112    pub fn into_struct(self) -> PokeStruct<'mem> {
113        match self {
114            Poke::Struct(s) => s,
115            _ => panic!("expected Struct variant"),
116        }
117    }
118
119    /// Converts this Poke into a PokeList, panicking if it's not a List variant
120    pub fn into_list(self) -> PokeListUninit<'mem> {
121        match self {
122            Poke::List(l) => l,
123            _ => panic!("expected List variant"),
124        }
125    }
126
127    /// Converts this Poke into a PokeMap, panicking if it's not a Map variant
128    pub fn into_map(self) -> PokeMapUninit<'mem> {
129        match self {
130            Poke::Map(m) => m,
131            _ => panic!("expected Map variant"),
132        }
133    }
134
135    /// Converts this Poke into a PokeValue, panicking if it's not a Scalar variant
136    pub fn into_scalar(self) -> PokeValue<'mem> {
137        match self {
138            Poke::Scalar(s) => s,
139            _ => panic!("expected Scalar variant"),
140        }
141    }
142
143    /// Converts this Poke into a PokeEnum, panicking if it's not an Enum variant
144    pub fn into_enum(self) -> PokeEnumNoVariant<'mem> {
145        match self {
146            Poke::Enum(e) => e,
147            _ => panic!("expected Enum variant"),
148        }
149    }
150
151    /// Converts into a value, so we can manipulate it
152    #[inline(always)]
153    pub fn into_value(self) -> PokeValue<'mem> {
154        match self {
155            Poke::Scalar(s) => s.into_value(),
156            Poke::List(l) => l.into_value(),
157            Poke::Map(m) => m.into_value(),
158            Poke::Struct(s) => s.into_value(),
159            Poke::Enum(e) => e.into_value(),
160        }
161    }
162
163    /// Get the shape of this Poke.
164    #[inline(always)]
165    pub fn shape(&self) -> &'static Shape {
166        match self {
167            Poke::Scalar(poke_value) => poke_value.shape(),
168            Poke::List(poke_list_uninit) => poke_list_uninit.shape(),
169            Poke::Map(poke_map_uninit) => poke_map_uninit.shape(),
170            Poke::Struct(poke_struct) => poke_struct.shape(),
171            Poke::Enum(poke_enum_no_variant) => poke_enum_no_variant.shape(),
172        }
173    }
174}
175
176/// Keeps track of which fields were initialized, up to 64 fields
177#[derive(Clone, Copy, Default)]
178pub struct ISet(u64);
179
180impl ISet {
181    /// Sets the bit at the given index.
182    pub fn set(&mut self, index: usize) {
183        if index >= 64 {
184            panic!("ISet can only track up to 64 fields. Index {index} is out of bounds.");
185        }
186        self.0 |= 1 << index;
187    }
188
189    /// Unsets the bit at the given index.
190    pub fn unset(&mut self, index: usize) {
191        if index >= 64 {
192            panic!("ISet can only track up to 64 fields. Index {index} is out of bounds.");
193        }
194        self.0 &= !(1 << index);
195    }
196
197    /// Checks if the bit at the given index is set.
198    pub fn has(&self, index: usize) -> bool {
199        if index >= 64 {
200            panic!("ISet can only track up to 64 fields. Index {index} is out of bounds.");
201        }
202        (self.0 & (1 << index)) != 0
203    }
204
205    /// Checks if all bits up to the given count are set.
206    pub fn all_set(&self, count: usize) -> bool {
207        if count > 64 {
208            panic!("ISet can only track up to 64 fields. Count {count} is out of bounds.");
209        }
210        let mask = (1 << count) - 1;
211        self.0 & mask == mask
212    }
213}