diff --git a/engine/src/ast/index_expr.rs b/engine/src/ast/index_expr.rs index 9f3a3540..641b8691 100644 --- a/engine/src/ast/index_expr.rs +++ b/engine/src/ast/index_expr.rs @@ -79,7 +79,9 @@ 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), } @@ -87,10 +89,14 @@ impl ValueExpr for IndexExpr { // 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); @@ -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); @@ -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 + } }) } } @@ -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<'_>| { @@ -232,6 +247,9 @@ impl IndexExpr { ) }, ) + } else { + TypedArray::new() + } }), } } @@ -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); diff --git a/engine/src/execution_context.rs b/engine/src/execution_context.rs index e5f390ae..3bb79062 100644 --- a/engine/src/execution_context.rs +++ b/engine/src/execution_context.rs @@ -43,7 +43,7 @@ pub struct InvalidListMatcherError { #[derive(Debug, PartialEq)] pub struct ExecutionContext<'e, U = ()> { scheme: Scheme, - values: Box<[Option>]>, + values: Box<[(bool, Option>)]>, list_matchers: Box<[Box]>, user_data: U, } @@ -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()) @@ -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(), @@ -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(), @@ -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 { + 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] @@ -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()); @@ -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)?; } }