use crate::plugin::exported_tables::entry::extensible::ExtensibleEntry;
use crate::plugin::exported_tables::entry::table_metadata::extensible::ExtensibleEntryMetadata;
use crate::plugin::exported_tables::entry::table_metadata::traits::TableMetadata;
use crate::plugin::exported_tables::entry::traits::Entry;
use crate::plugin::exported_tables::field_descriptor::{FieldDescriptor, FieldRef};
use crate::plugin::exported_tables::field_value::dynamic::DynamicFieldValue;
use crate::plugin::exported_tables::metadata::HasMetadata;
use crate::plugin::exported_tables::metadata::Metadata;
use crate::plugin::exported_tables::ref_shared::{
new_counted_ref, new_shared_ref, RefCounted, RefGuard, RefShared,
};
use crate::plugin::exported_tables::vtable::Vtable;
use crate::plugin::tables::data::{FieldTypeId, Key};
use crate::FailureReason;
use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_fieldinfo};
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::ffi::CStr;
use std::fmt::{Debug, Formatter};
#[must_use]
pub struct Table<K, E>
where
K: Key + Ord,
K: Borrow<<K as Key>::Borrowed>,
<K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
E: Entry,
E::Metadata: TableMetadata,
{
name: &'static CStr,
field_descriptors: Vec<ss_plugin_table_fieldinfo>,
metadata: RefShared<ExtensibleEntryMetadata<E::Metadata>>,
data: RefShared<BTreeMap<K, RefShared<ExtensibleEntry<E>>>>,
pub(in crate::plugin::exported_tables) vtable: RefCounted<Option<Box<Vtable>>>,
}
impl<K, E> Debug for Table<K, E>
where
K: Key + Ord + Debug,
K: Borrow<<K as Key>::Borrowed>,
<K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
E: Entry + Debug,
E::Metadata: TableMetadata + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Table")
.field("name", &self.name)
.field("metadata", &self.metadata)
.field("data", &self.data)
.finish()
}
}
type TableMetadataType<E> = RefShared<ExtensibleEntryMetadata<<E as HasMetadata>::Metadata>>;
pub(in crate::plugin::exported_tables) type TableEntryType<E> = RefGuard<ExtensibleEntry<E>>;
impl<K, E> Table<K, E>
where
K: Key + Ord,
K: Borrow<<K as Key>::Borrowed>,
<K as Key>::Borrowed: Ord + ToOwned<Owned = K>,
E: Entry,
E::Metadata: TableMetadata,
{
pub fn new_with_metadata(
tag: &'static CStr,
metadata: &TableMetadataType<E>,
) -> Result<Self, anyhow::Error> {
let table = Self {
name: tag,
field_descriptors: vec![],
metadata: metadata.clone(),
data: new_shared_ref(BTreeMap::new()),
vtable: new_counted_ref(None),
};
Ok(table)
}
pub fn new(name: &'static CStr) -> Result<Self, anyhow::Error> {
Ok(Self {
name,
field_descriptors: vec![],
metadata: new_shared_ref(ExtensibleEntryMetadata::new()?),
data: new_shared_ref(BTreeMap::new()),
vtable: new_counted_ref(None),
})
}
pub fn data(&self) -> RefShared<BTreeMap<K, RefShared<ExtensibleEntry<E>>>> {
self.data.clone()
}
pub fn name(&self) -> &'static CStr {
self.name
}
pub fn size(&self) -> usize {
self.data.read().len()
}
pub fn lookup<Q>(&self, key: &Q) -> Option<TableEntryType<E>>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
Some(self.data.read().get(key)?.write_arc())
}
pub fn get_field_value(
&self,
entry: &TableEntryType<E>,
field: &FieldDescriptor,
out: &mut ss_plugin_state_data,
) -> Result<(), anyhow::Error> {
let (type_id, index) = { (field.type_id, field.index) };
entry.get(index, type_id, out)
}
pub fn iterate_entries<F>(&mut self, mut func: F) -> bool
where
F: FnMut(&mut TableEntryType<E>) -> bool,
{
for value in &mut self.data.write().values_mut() {
if !func(&mut value.write_arc()) {
return false;
}
}
true
}
pub fn clear(&mut self) {
self.data.write().clear()
}
pub fn erase<Q>(&mut self, key: &Q) -> Option<TableEntryType<E>>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
Some(self.data.write().remove(key)?.write_arc())
}
pub fn create_entry(&self) -> Result<TableEntryType<E>, anyhow::Error> {
Ok(new_shared_ref(ExtensibleEntry::new_with_metadata(
self.name,
&self.metadata,
)?)
.write_arc())
}
pub fn create_entry_fn(
&self,
) -> impl Fn() -> Result<RefShared<ExtensibleEntry<E>>, anyhow::Error> + use<K, E> {
let name = self.name;
let metadata = self.metadata.clone();
move || {
Ok(new_shared_ref(ExtensibleEntry::new_with_metadata(
name, &metadata,
)?))
}
}
pub fn insert<Q>(&mut self, key: &Q, entry: TableEntryType<E>) -> Option<TableEntryType<E>>
where
K: Borrow<Q>,
Q: Ord + ToOwned<Owned = K> + ?Sized,
{
let new_entry = std::sync::Arc::clone(RefGuard::rwlock(&entry));
self.data
.write()
.insert(key.to_owned(), std::sync::Arc::clone(&new_entry));
drop(entry);
Some(new_entry.write_arc())
}
pub fn write(
&self,
entry: &mut TableEntryType<E>,
field: &FieldDescriptor,
value: &ss_plugin_state_data,
) -> Result<(), anyhow::Error> {
if field.read_only {
return Err(anyhow::anyhow!("Field is read-only").context(FailureReason::NotSupported));
}
let (type_id, index) = { (field.type_id, field.index) };
let value = unsafe {
DynamicFieldValue::from_data(value, type_id).ok_or_else(|| {
anyhow::anyhow!("Cannot store {:?} data (unsupported type)", type_id)
})?
};
entry.set(index, value)
}
pub fn list_fields(&mut self) -> &[ss_plugin_table_fieldinfo] {
self.field_descriptors.clear();
self.field_descriptors.extend(self.metadata.list_fields());
self.field_descriptors.as_slice()
}
pub fn get_field(&self, name: &CStr, field_type: FieldTypeId) -> Option<FieldRef> {
self.metadata
.get_field(name)
.filter(|f| f.as_ref().type_id == field_type)
}
pub fn add_field(
&mut self,
name: &CStr,
field_type: FieldTypeId,
read_only: bool,
) -> Option<FieldRef> {
self.metadata.add_field(name, field_type, read_only)
}
}
#[cfg(test)]
mod tests {
use crate::plugin::exported_tables::entry::dynamic::DynamicEntry;
use crate::tables::export::Table;
use crate::tables::import::Bool;
use crate::tables::TablesInput;
use std::ffi::CString;
#[allow(unused)]
fn add_table(input: &TablesInput) -> anyhow::Result<()> {
input.add_table(Table::<i8, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<i16, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<i32, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<i64, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<u8, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<u16, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<u32, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<u64, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<Bool, DynamicEntry>::new(c"exported")?)?;
input.add_table(Table::<CString, DynamicEntry>::new(c"exported")?)?;
Ok(())
}
}