Skip to main content

falco_plugin/tables/export/
table_box.rs

1use crate::tables::export::entry::extensible::ExtensibleEntry;
2use crate::tables::export::table::{TableData, TableEntryType};
3use crate::tables::export::traits::{Entry, TableMetadata};
4use crate::tables::export::{FieldRef, HasMetadata, RefShared};
5use crate::tables::{FieldTypeId, Key};
6use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_fieldinfo, ss_plugin_table_input};
7use std::borrow::Borrow;
8use std::collections::BTreeMap;
9use std::ffi::CStr;
10use std::fmt::{Debug, Formatter};
11use std::ops::{Deref, DerefMut};
12use std::ptr::NonNull;
13
14/// # A table exported to other plugins
15///
16/// An instance of this type can be exposed to other plugins via
17/// [`tables::TablesInput::add_table`](`crate::tables::TablesInput::add_table`)
18///
19/// The generic parameters are: key type and entry type. The key type is anything
20/// usable as a table key, while the entry type is a type that can be stored in the table.
21/// You can obtain such a type by `#[derive]`ing Entry on a struct describing all the table fields.
22///
23/// Supported key types include:
24/// - integer types (u8/i8, u16/i16, u32/i32, u64/i64)
25/// - [`crate::tables::import::Bool`] (an API equivalent of bool)
26/// - &CStr (spelled as just `CStr` when used as a generic argument)
27///
28/// See [`crate::tables::export`] for details.
29///
30/// The implementation is thread-safe when the `thread-safe-tables` feature is enabled.
31///
32/// Internally, this uses `NonNull` instead of `Box` to avoid Miri's Stacked Borrows
33/// transitive retagging, which would conflict with FFI callbacks that access the table
34/// through raw pointers.
35pub struct Table<K, E>
36where
37    K: Key + Ord,
38    K: Borrow<<K as Key>::Borrowed>,
39    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
40    E: Entry,
41    E::Metadata: TableMetadata,
42{
43    ptr: NonNull<TableData<K, E>>,
44}
45
46impl<K, E> Table<K, E>
47where
48    K: Key + Ord,
49    K: Borrow<<K as Key>::Borrowed>,
50    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
51    E: Entry,
52    E::Metadata: TableMetadata,
53{
54    /// Wrap a `TableData` into a `Table`.
55    pub(crate) fn wrap(value: TableData<K, E>) -> Self {
56        let ptr = Box::into_raw(Box::new(value));
57        // SAFETY: Box::into_raw never returns null
58        Self {
59            ptr: unsafe { NonNull::new_unchecked(ptr) },
60        }
61    }
62
63    /// Returns a raw mutable pointer to the contained data without creating a reference.
64    pub(crate) fn as_mut_ptr(this: &Self) -> *mut TableData<K, E> {
65        this.ptr.as_ptr()
66    }
67
68    /// Get or create the vtable for this table, for use in FFI.
69    pub(crate) fn get_vtable(&self) -> *mut ss_plugin_table_input {
70        let table_ptr = Self::as_mut_ptr(self);
71        (**self).get_vtable_with_ptr(table_ptr)
72    }
73
74    /// Create a new table
75    pub fn new(name: &'static CStr) -> Result<Self, anyhow::Error> {
76        Ok(Self::wrap(TableData::new(name)?))
77    }
78
79    /// Create a new table using provided metadata
80    ///
81    /// This is only expected to be used by the derive macro.
82    pub fn new_with_metadata(
83        tag: &'static CStr,
84        metadata: &<Self as HasMetadata>::Metadata,
85    ) -> Result<Self, anyhow::Error> {
86        Ok(Self::wrap(TableData::new_with_metadata(tag, metadata)?))
87    }
88
89    /// Get an accessor to the underlying data
90    ///
91    /// This method returns a reference to the underlying BTreeMap, containing all the table's data.
92    /// It can be useful for:
93    /// - accessing the table from a different thread (with the `thread-safe-tables` feature enabled)
94    /// - bypassing the table API for convenience or more control over locking
95    ///
96    /// To actually access the BTreeMap, you first need to lock the returned object for reading
97    /// (`data.read()`) or writing (`data.write()`).
98    pub fn data(&self) -> RefShared<BTreeMap<K, RefShared<ExtensibleEntry<E>>>> {
99        (**self).data()
100    }
101
102    /// Return the table name.
103    pub fn name(&self) -> &'static CStr {
104        (**self).name()
105    }
106
107    /// Return the number of entries in the table.
108    pub fn size(&self) -> usize {
109        (**self).size()
110    }
111
112    /// Get an entry corresponding to a particular key.
113    pub fn lookup<Q>(&self, key: &Q) -> Option<TableEntryType<E>>
114    where
115        K: Borrow<Q>,
116        Q: Ord + ?Sized,
117    {
118        (**self).lookup(key)
119    }
120
121    /// Get the value for a field in an entry.
122    pub fn get_field_value(
123        &self,
124        entry: &TableEntryType<E>,
125        field: &crate::tables::export::field_descriptor::FieldDescriptor,
126        out: &mut ss_plugin_state_data,
127    ) -> Result<(), anyhow::Error> {
128        (**self).get_field_value(entry, field, out)
129    }
130
131    /// Execute a closure on all entries in the table with read-only access.
132    ///
133    /// The iteration continues until all entries are visited or the closure returns false.
134    // TODO(upstream) the closure cannot store away the entry but we could use explicit docs
135    pub fn iterate_entries<F>(&self, func: F) -> bool
136    where
137        F: FnMut(&mut TableEntryType<E>) -> bool,
138    {
139        (**self).iterate_entries(func)
140    }
141
142    /// Remove all entries from the table.
143    pub fn clear(&mut self) {
144        (**self).clear()
145    }
146
147    /// Erase an entry by key.
148    pub fn erase<Q>(&mut self, key: &Q) -> Option<TableEntryType<E>>
149    where
150        K: Borrow<Q>,
151        Q: Ord + ?Sized,
152    {
153        (**self).erase(key)
154    }
155
156    /// Create a new table entry.
157    ///
158    /// This is a detached entry that can be later inserted into the table using [`Table::insert`].
159    pub fn create_entry(&self) -> Result<TableEntryType<E>, anyhow::Error> {
160        (**self).create_entry()
161    }
162
163    /// Return a closure for creating table entries
164    ///
165    /// The `Table` object itself cannot be shared between threads safely even with
166    /// the `thread-safe-tables` feature enabled, but almost full functionality can be achieved
167    /// using two objects that can:
168    /// 1. The underlying BTreeMap, obtained from [Table::data]
169    /// 2. A closure capable of creating a new entry (returned from this function)
170    ///
171    /// The only functionality missing is listing table fields, and until a use case comes along,
172    /// it's likely to remain unimplemented.
173    ///
174    /// The entry obtained by calling the closure returned from `create_entry_fn` can be later
175    /// inserted into the table e.g. by calling [BTreeMap::insert].
176    ///
177    /// To actually access the entry's fields, you first need to lock the returned object for reading
178    /// (`data.read()`) or writing (`data.write()`).
179    pub fn create_entry_fn(
180        &self,
181    ) -> impl Fn() -> Result<RefShared<ExtensibleEntry<E>>, anyhow::Error> + use<K, E> {
182        (**self).create_entry_fn()
183    }
184
185    /// Attach an entry to a table key
186    pub fn insert<Q>(&mut self, key: &Q, entry: TableEntryType<E>) -> Option<TableEntryType<E>>
187    where
188        K: Borrow<Q>,
189        Q: Ord + ToOwned<Owned = K> + ?Sized,
190    {
191        (**self).insert(key, entry)
192    }
193
194    /// Write a value to a field of an entry
195    pub fn write(
196        &self,
197        entry: &mut TableEntryType<E>,
198        field: &crate::tables::export::field_descriptor::FieldDescriptor,
199        value: &ss_plugin_state_data,
200    ) -> Result<(), anyhow::Error> {
201        (**self).write(entry, field, value)
202    }
203
204    /// Return a list of fields as a slice of raw FFI objects
205    pub fn list_fields(&self) -> &[ss_plugin_table_fieldinfo] {
206        (**self).list_fields()
207    }
208
209    /// Return a field descriptor for a particular field
210    ///
211    /// The requested `field_type` must match the actual type of the field
212    pub fn get_field(&self, name: &CStr, field_type: FieldTypeId) -> Option<FieldRef> {
213        (**self).get_field(name, field_type)
214    }
215
216    /// Add a new field to the table
217    pub fn add_field(
218        &self,
219        name: &CStr,
220        field_type: FieldTypeId,
221        read_only: bool,
222    ) -> Option<FieldRef> {
223        (**self).add_field(name, field_type, read_only)
224    }
225}
226
227impl<K, E> Deref for Table<K, E>
228where
229    K: Key + Ord,
230    K: Borrow<<K as Key>::Borrowed>,
231    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
232    E: Entry,
233    E::Metadata: TableMetadata,
234{
235    type Target = TableData<K, E>;
236    fn deref(&self) -> &TableData<K, E> {
237        // SAFETY: the pointer is valid as long as self is alive
238        unsafe { self.ptr.as_ref() }
239    }
240}
241
242impl<K, E> DerefMut for Table<K, E>
243where
244    K: Key + Ord,
245    K: Borrow<<K as Key>::Borrowed>,
246    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
247    E: Entry,
248    E::Metadata: TableMetadata,
249{
250    fn deref_mut(&mut self) -> &mut TableData<K, E> {
251        // SAFETY: the pointer is valid as long as self is alive and we have &mut self
252        unsafe { self.ptr.as_mut() }
253    }
254}
255
256impl<K, E> Drop for Table<K, E>
257where
258    K: Key + Ord,
259    K: Borrow<<K as Key>::Borrowed>,
260    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
261    E: Entry,
262    E::Metadata: TableMetadata,
263{
264    fn drop(&mut self) {
265        // SAFETY: we own the allocation and it hasn't been freed
266        unsafe {
267            drop(Box::from_raw(self.ptr.as_ptr()));
268        }
269    }
270}
271
272impl<K, E> Debug for Table<K, E>
273where
274    K: Key + Ord + Debug,
275    K: Borrow<<K as Key>::Borrowed>,
276    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
277    E: Entry + Debug,
278    E::Metadata: TableMetadata + Debug,
279{
280    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
281        (**self).fmt(f)
282    }
283}
284
285// SAFETY: Table has the same semantics as Box
286unsafe impl<K, E> Send for Table<K, E>
287where
288    K: Key + Ord + Send,
289    K: Borrow<<K as Key>::Borrowed>,
290    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
291    E: Entry + Send,
292    E::Metadata: TableMetadata,
293{
294}
295unsafe impl<K, E> Sync for Table<K, E>
296where
297    K: Key + Ord + Sync,
298    K: Borrow<<K as Key>::Borrowed>,
299    <K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
300    E: Entry + Sync,
301    E::Metadata: TableMetadata,
302{
303}