use crate::plugin::error::as_result::{AsResult, WithLastError};
use crate::plugin::tables::data::{FieldTypeId, Key, Value};
use crate::plugin::tables::entry::raw::RawEntry;
use crate::plugin::tables::field::raw::RawField;
use crate::plugin::tables::traits::TableMetadata;
use crate::plugin::tables::vtable::fields::TableFields;
use crate::plugin::tables::vtable::reader::private::TableReaderImpl;
use crate::plugin::tables::vtable::reader::TableReader;
use crate::plugin::tables::vtable::writer::private::TableWriterImpl;
use crate::plugin::tables::vtable::writer::TableWriter;
use crate::plugin::tables::vtable::TablesInput;
use crate::strings::from_ptr::try_str_from_ptr_with_lifetime;
use falco_plugin_api::{
ss_plugin_bool, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_state_data, ss_plugin_state_type,
ss_plugin_table_entry_t, ss_plugin_table_field_t, ss_plugin_table_fieldinfo,
ss_plugin_table_iterator_func_t, ss_plugin_table_iterator_state_t, ss_plugin_table_t,
};
use num_traits::FromPrimitive;
use std::ffi::CStr;
use std::ops::ControlFlow;
struct TemporaryTableEntry<'a> {
tables_input: &'a TablesInput<'a>,
table: *mut ss_plugin_table_t,
entry: *mut ss_plugin_table_entry_t,
}
impl TemporaryTableEntry<'_> {
fn create<'a>(
tables_input: &'a TablesInput<'a>,
table: *mut ss_plugin_table_t,
) -> Result<TemporaryTableEntry<'a>, anyhow::Error> {
let entry = unsafe { tables_input.writer_ext.create_table_entry(table)? };
if entry.is_null() {
anyhow::bail!("Failed to create temporary table entry");
}
Ok(TemporaryTableEntry {
tables_input,
table,
entry,
})
}
}
impl Drop for TemporaryTableEntry<'_> {
fn drop(&mut self) {
unsafe {
self.tables_input
.writer_ext
.destroy_table_entry(self.table, self.entry);
}
}
}
#[derive(Debug)]
pub struct RawTable {
pub(crate) table: *mut ss_plugin_table_t,
}
impl RawTable {
pub fn list_fields(&self, fields_vtable: &TableFields) -> &[ss_plugin_table_fieldinfo] {
let mut num_fields = 0u32;
let fields = fields_vtable
.list_table_fields(self.table, &mut num_fields as *mut _)
.unwrap_or(std::ptr::null_mut());
if fields.is_null() {
&[]
} else {
unsafe { std::slice::from_raw_parts(fields, num_fields as usize) }
}
}
pub fn get_field<V: Value + ?Sized>(
&self,
tables_input: &TablesInput,
name: &CStr,
) -> Result<RawField<V>, anyhow::Error> {
let field = tables_input.fields_ext.get_table_field(
self.table,
name.as_ptr().cast(),
V::TYPE_ID as ss_plugin_state_type,
)?;
let raw_field = unsafe {
field
.as_mut()
.ok_or_else(|| anyhow::anyhow!("Failed to get table field {:?}", name))
.with_last_error(&tables_input.last_error)?;
field
};
let assoc = unsafe { V::get_assoc_from_raw_table(self, raw_field, tables_input) }?;
Ok(RawField {
field: raw_field,
assoc_data: assoc,
})
}
pub fn add_field<V: Value<AssocData = ()> + ?Sized>(
&self,
tables_input: &TablesInput,
name: &CStr,
) -> Result<RawField<V>, anyhow::Error> {
let field = tables_input.fields_ext.add_table_field(
self.table,
name.as_ptr().cast(),
V::TYPE_ID as ss_plugin_state_type,
)?;
let raw_field = unsafe {
field
.as_mut()
.ok_or_else(|| anyhow::anyhow!("Failed to add table field {:?}", name))
.with_last_error(&tables_input.last_error)?;
field
};
Ok(RawField {
field: raw_field,
assoc_data: (),
})
}
pub fn get_entry<K: Key>(
&self,
reader_vtable: &impl TableReader,
key: &K,
) -> Result<RawEntry, anyhow::Error> {
let input = unsafe { &*(self.table as *mut falco_plugin_api::ss_plugin_table_input) };
if input.key_type != K::TYPE_ID as ss_plugin_state_type {
anyhow::bail!(
"Bad key type, requested {:?}, table has {:?}",
K::TYPE_ID,
FieldTypeId::from_u32(input.key_type),
);
}
let entry =
unsafe { reader_vtable.get_table_entry(self.table, &key.to_data() as *const _) }?;
if entry.is_null() {
Err(anyhow::anyhow!("table entry not found"))
} else {
Ok(RawEntry {
table: self.table,
entry: entry as *mut _,
destructor: reader_vtable.release_table_entry_fn(),
})
}
}
pub unsafe fn erase<K: Key>(
&self,
writer_vtable: &impl TableWriter,
key: &K,
) -> Result<(), anyhow::Error> {
unsafe {
writer_vtable
.erase_table_entry(self.table, &key.to_data() as *const _)?
.as_result()?
};
Ok(())
}
pub fn create_entry(
&self,
writer_vtable: &impl TableWriter,
) -> Result<RawEntry, anyhow::Error> {
let entry = unsafe { writer_vtable.create_table_entry(self.table) }?;
if entry.is_null() {
Err(anyhow::anyhow!("Failed to create table entry"))
} else {
Ok(RawEntry {
table: self.table,
entry,
destructor: writer_vtable.destroy_table_entry_fn(),
})
}
}
pub unsafe fn insert<K: Key>(
&self,
reader_vtable: &impl TableReader,
writer_vtable: &impl TableWriter,
key: &K,
mut entry: RawEntry,
) -> Result<RawEntry, anyhow::Error> {
let ret = unsafe {
writer_vtable.add_table_entry(self.table, &key.to_data() as *const _, entry.entry)?
};
if ret.is_null() {
Err(anyhow::anyhow!("Failed to attach entry"))
} else {
entry.destructor.take();
Ok(RawEntry {
table: self.table,
entry: ret,
destructor: reader_vtable.release_table_entry_fn(),
})
}
}
pub fn get_name(&self, reader_vtable: &impl TableReader) -> anyhow::Result<&str> {
unsafe {
Ok(try_str_from_ptr_with_lifetime(
reader_vtable.get_table_name(self.table)?,
self,
)?)
}
}
pub fn get_size(&self, reader_vtable: &impl TableReader) -> anyhow::Result<usize> {
Ok(unsafe { reader_vtable.get_table_size(self.table) }? as usize)
}
pub fn iter_entries_mut<F>(
&self,
reader_vtable: &impl TableReader,
mut func: F,
) -> anyhow::Result<IterationResult>
where
F: FnMut(RawEntry) -> ControlFlow<()>,
{
Ok(iter_inner(
self.table,
reader_vtable.iterate_entries_fn()?,
move |s: *mut ss_plugin_table_entry_t| {
let raw_entry = RawEntry {
table: self.table,
entry: s,
destructor: None,
};
func(raw_entry).is_continue()
},
))
}
pub fn clear(&self, writer_vtable: &impl TableWriter) -> Result<(), anyhow::Error> {
Ok(unsafe { writer_vtable.clear_table(self.table) }?.as_result()?)
}
pub(in crate::plugin::tables) unsafe fn with_subtable<K, F, R>(
&self,
field: *mut ss_plugin_table_field_t,
tables_input: &TablesInput,
func: F,
) -> Result<R, anyhow::Error>
where
K: Key,
F: FnOnce(&RawTable) -> R,
{
let entry = TemporaryTableEntry::create(tables_input, self.table)?;
let mut val = ss_plugin_state_data { u64_: 0 };
let rc = unsafe {
tables_input.reader_ext.read_entry_field(
self.table,
entry.entry,
field,
&mut val as *mut _,
)?
};
if rc != ss_plugin_rc_SS_PLUGIN_SUCCESS {
anyhow::bail!("Failed to get field value for temporary table entry")
}
let input = unsafe { val.table } as *mut falco_plugin_api::ss_plugin_table_input;
let input = unsafe { input.as_mut() };
let Some(input) = input else {
anyhow::bail!("Temporary table entry has a null value for table");
};
if input.key_type != K::TYPE_ID as ss_plugin_state_type {
anyhow::bail!(
"Bad key type, requested {:?}, table has {:?}",
K::TYPE_ID,
FieldTypeId::from_u32(input.key_type),
);
}
let raw_table = unsafe { RawTable { table: val.table } };
let ret = func(&raw_table);
Ok(ret)
}
#[doc(hidden)]
pub fn get_metadata<K: Key, M: TableMetadata, V: Value + ?Sized>(
&self,
field: &RawField<V>,
tables_input: &TablesInput,
) -> Result<M, anyhow::Error> {
unsafe {
self.with_subtable::<K, _, _>(field.field, tables_input, |subtable| {
M::new(subtable, tables_input)
})
}?
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum IterationResult {
Finished,
Exited,
}
fn iter_inner<F>(
table: *mut ss_plugin_table_t,
iterate_entries: unsafe extern "C-unwind" fn(
*mut ss_plugin_table_t,
it: ss_plugin_table_iterator_func_t,
s: *mut ss_plugin_table_iterator_state_t,
) -> ss_plugin_bool,
mut func: F,
) -> IterationResult
where
F: FnMut(*mut ss_plugin_table_entry_t) -> bool,
{
extern "C-unwind" fn iter_wrapper<WF>(
s: *mut ss_plugin_table_iterator_state_t,
entry: *mut ss_plugin_table_entry_t,
) -> ss_plugin_bool
where
WF: FnMut(*mut ss_plugin_table_entry_t) -> bool,
{
unsafe {
let Some(closure) = (s as *mut WF).as_mut() else {
return 0;
};
let res = closure(entry);
if res {
1
} else {
0
}
}
}
let finished = unsafe {
iterate_entries(
table,
Some(iter_wrapper::<F>),
&mut func as *mut _ as *mut ss_plugin_table_iterator_state_t,
) != 0
};
match finished {
true => IterationResult::Finished,
false => IterationResult::Exited,
}
}