Skip to content

[RFC] Support executing filters against fields without a value #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions engine/src/ast/index_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,24 @@ impl ValueExpr for IndexExpr {
// Fast path
match identifier {
IdentifierExpr::Field(f) => CompiledValueExpr::new(move |ctx| {
Ok(ctx.get_field_value_unchecked(&f).as_ref())
ctx.get_marked_field_value(&f)
.map(LhsValue::as_ref)
.ok_or(ty)
}),
IdentifierExpr::FunctionCallExpr(call) => compiler.compile_function_call_expr(call),
}
} else if let Some(last) = last {
// Average path
match identifier {
IdentifierExpr::Field(f) => CompiledValueExpr::new(move |ctx| {
ctx.get_field_value_unchecked(&f)
.get_nested(&indexes[..last])
.map(LhsValue::as_ref)
.ok_or(ty)
if let Some(value) = ctx.get_marked_field_value(&f) {
value
.get_nested(&indexes[..last])
.map(LhsValue::as_ref)
.ok_or(ty)
} else {
Err(ty)
}
}),
IdentifierExpr::FunctionCallExpr(call) => {
let call = compiler.compile_function_call_expr(call);
Expand All @@ -106,9 +112,13 @@ impl ValueExpr for IndexExpr {
// Slow path
match identifier {
IdentifierExpr::Field(f) => CompiledValueExpr::new(move |ctx| {
let mut iter = MapEachIterator::from_indexes(&indexes[..]);
iter.reset(ctx.get_field_value_unchecked(&f).as_ref());
Ok(LhsValue::Array(Array::try_from_iter(ty, iter).unwrap()))
if let Some(value) = ctx.get_marked_field_value(&f) {
let mut iter = MapEachIterator::from_indexes(&indexes[..]);
iter.reset(value.as_ref());
Ok(LhsValue::Array(Array::try_from_iter(ty, iter).unwrap()))
} else {
Ok(LhsValue::Array(Array::new(ty)))
}
}),
IdentifierExpr::FunctionCallExpr(call) => {
let call = compiler.compile_function_call_expr(call);
Expand Down Expand Up @@ -174,17 +184,23 @@ impl IndexExpr {
IdentifierExpr::Field(f) => {
if indexes.is_empty() {
CompiledOneExpr::new(move |ctx| {
comp.compare(ctx.get_field_value_unchecked(&f), ctx)
if let Some(value) = ctx.get_marked_field_value(&f) {
comp.compare(value, ctx)
} else {
default
}
})
} else {
CompiledOneExpr::new(move |ctx| {
ctx.get_field_value_unchecked(&f)
.get_nested(&indexes)
.map_or(
if let Some(value) = ctx.get_marked_field_value(&f) {
value.get_nested(&indexes).map_or(
default,
#[inline]
|val| comp.compare(val, ctx),
)
} else {
default
}
})
}
}
Expand Down Expand Up @@ -221,9 +237,8 @@ impl IndexExpr {
}
IdentifierExpr::Field(f) => CompiledVecExpr::new(move |ctx| {
let comp = ∁
ctx.get_field_value_unchecked(&f)
.get_nested(&indexes)
.map_or(
if let Some(value) = ctx.get_marked_field_value(&f) {
value.get_nested(&indexes).map_or(
BOOL_ARRAY,
#[inline]
|val: &LhsValue<'_>| {
Expand All @@ -232,6 +247,9 @@ impl IndexExpr {
)
},
)
} else {
TypedArray::new()
}
}),
}
}
Expand All @@ -247,9 +265,13 @@ impl IndexExpr {
} = self;
match identifier {
IdentifierExpr::Field(f) => CompiledVecExpr::new(move |ctx| {
let mut iter = MapEachIterator::from_indexes(&indexes[..]);
iter.reset(ctx.get_field_value_unchecked(&f).as_ref());
TypedArray::from_iter(iter.map(|item| comp.compare(&item, ctx)))
if let Some(value) = ctx.get_marked_field_value(&f) {
let mut iter = MapEachIterator::from_indexes(&indexes[..]);
iter.reset(value.as_ref());
TypedArray::from_iter(iter.map(|item| comp.compare(&item, ctx)))
} else {
TypedArray::new()
}
}),
IdentifierExpr::FunctionCallExpr(call) => {
let call = compiler.compile_function_call_expr(call);
Expand Down
51 changes: 34 additions & 17 deletions engine/src/execution_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct InvalidListMatcherError {
#[derive(Debug, PartialEq)]
pub struct ExecutionContext<'e, U = ()> {
scheme: Scheme,
values: Box<[Option<LhsValue<'e>>]>,
values: Box<[(bool, Option<LhsValue<'e>>)]>,
list_matchers: Box<[Box<dyn ListMatcher>]>,
user_data: U,
}
Expand All @@ -65,7 +65,7 @@ impl<'e, U> ExecutionContext<'e, U> {
pub fn new_with(scheme: &Scheme, f: impl FnOnce() -> U) -> Self {
ExecutionContext {
scheme: scheme.clone(),
values: vec![None; scheme.field_count()].into(),
values: vec![(false, None); scheme.field_count()].into(),
list_matchers: scheme
.lists()
.map(|list| list.definition().new_matcher())
Expand Down Expand Up @@ -94,7 +94,9 @@ impl<'e, U> ExecutionContext<'e, U> {
let value_type = value.get_type();

if field_type == value_type {
Ok(self.values[field.index()].replace(value))
let (marked, old) = &mut self.values[field.index()];
*marked = true;
Ok(old.replace(value))
} else {
Err(SetFieldValueError::TypeMismatch(TypeMismatchError {
expected: field_type.into(),
Expand All @@ -119,7 +121,9 @@ impl<'e, U> ExecutionContext<'e, U> {
let value_type = value.get_type();

if field_type == value_type {
Ok(self.values[field.index()].replace(value))
let (marked, old) = &mut self.values[field.index()];
*marked = true;
Ok(old.replace(value))
} else {
Err(SetFieldValueError::TypeMismatch(TypeMismatchError {
expected: field_type.into(),
Expand All @@ -128,29 +132,39 @@ impl<'e, U> ExecutionContext<'e, U> {
}
}

/// Mark a field as ready for matching. Only useful for fields without value.
pub fn mark_field_value(&mut self, field: FieldRef<'_>) -> Result<bool, SetFieldValueError> {
if self.scheme != *field.scheme() {
return Err(SetFieldValueError::SchemeMismatch(SchemeMismatchError));
}

let marked = self.values[field.index()].0;
self.values[field.index()].0 = true;

Ok(marked)
}

#[inline]
pub(crate) fn get_field_value_unchecked(&self, field: &Field) -> &LhsValue<'_> {
pub(crate) fn get_marked_field_value(&self, field: &Field) -> Option<&LhsValue<'_>> {
// This is safe because this code is reachable only from Filter::execute
// which already performs the scheme compatibility check, but check that
// invariant holds in the future at least in the debug mode.
debug_assert!(self.scheme() == field.scheme());

// For now we panic in this, but later we are going to align behaviour
// with wireshark: resolve all subexpressions that don't have RHS value
// to `false`.
self.values[field.index()].as_ref().unwrap_or_else(|| {
panic!(
"Field {} was registered but not given a value",
field.name()
);
})
let (marked, value) = &self.values[field.index()];

if !*marked {
panic!("Field {} was registered but marked as ready", field.name());
}

value.as_ref()
}

/// Get the value of a field.
pub fn get_field_value(&self, field: FieldRef<'_>) -> Option<&LhsValue<'_>> {
assert!(self.scheme() == field.scheme());

self.values[field.index()].as_ref()
self.values[field.index()].1.as_ref()
}

#[inline]
Expand Down Expand Up @@ -221,7 +235,9 @@ impl<'e, U> ExecutionContext<'e, U> {
/// while retaining the allocated memory.
#[inline]
pub fn clear(&mut self) {
self.values.iter_mut().for_each(|value| *value = None);
self.values
.iter_mut()
.for_each(|value| *value = (false, None));
self.list_matchers
.iter_mut()
.for_each(|list_matcher| list_matcher.clear());
Expand Down Expand Up @@ -356,7 +372,8 @@ impl Serialize for ExecutionContext<'_> {
{
let mut map = serializer.serialize_map(Some(self.values.len()))?;
for field in self.scheme().fields() {
if let Some(Some(value)) = self.values.get(field.index()) {
// Do we want to serialize null for marked fields without a value?
if let (_, Some(value)) = &self.values[field.index()] {
map.serialize_entry(field.name(), value)?;
}
}
Expand Down
Loading