use crate::LibmagicError;
use crate::evaluator::{operators, types};
use crate::parser::ast::{MagicRule, TypeKind};
use log::debug;
pub(crate) fn evaluate_pattern_rule(
rule: &MagicRule,
buffer: &[u8],
absolute_offset: usize,
max_string_length: usize,
) -> Result<(bool, crate::parser::ast::Value), LibmagicError> {
let match_outcome = types::read_pattern_match(
buffer,
absolute_offset,
&rule.typ,
Some(&rule.value),
max_string_length,
)
.map_err(|e| LibmagicError::EvaluationError(e.into()))?;
let pattern_found = match_outcome.is_some();
let matched = match &rule.op {
crate::parser::ast::Operator::Equal => pattern_found,
crate::parser::ast::Operator::NotEqual => !pattern_found,
other => {
return Err(LibmagicError::EvaluationError(
types::TypeReadError::UnsupportedType {
type_name: format!(
"operator {other:?} is not supported for pattern-bearing type {:?}; only Equal (=) and NotEqual (!=) are allowed",
rule.typ
),
}
.into(),
));
}
};
let value = match_outcome.unwrap_or_else(|| crate::parser::ast::Value::String(String::new()));
Ok((matched, value))
}
pub(crate) fn evaluate_value_rule(
rule: &MagicRule,
buffer: &[u8],
absolute_offset: usize,
max_string_length: usize,
flip_endian: bool,
) -> Result<(bool, crate::parser::ast::Value), LibmagicError> {
let read_typ: std::borrow::Cow<'_, crate::parser::ast::TypeKind> = if flip_endian {
std::borrow::Cow::Owned(types::flip_type_endian(&rule.typ))
} else {
std::borrow::Cow::Borrowed(&rule.typ)
};
let read_value = types::read_typed_value_with_pattern(
buffer,
absolute_offset,
read_typ.as_ref(),
Some(&rule.value),
max_string_length,
)
.map_err(|e| LibmagicError::EvaluationError(e.into()))?;
let transformed_value = match rule.value_transform {
None => read_value,
Some(t) => operators::apply_value_transform(&read_value, t)
.map_err(LibmagicError::EvaluationError)?,
};
let expected_value = types::coerce_value_to_type(&rule.value, &rule.typ);
let expected_ref: &crate::parser::ast::Value = expected_value.as_ref();
let matched = match &rule.op {
crate::parser::ast::Operator::BitwiseNot => operators::apply_bitwise_not_with_width(
&transformed_value,
expected_ref,
rule.typ.bit_width(),
),
crate::parser::ast::Operator::BitwiseAndMask(mask) => {
operators::apply_bitwise_and_mask_with_width(
*mask,
&transformed_value,
expected_ref,
rule.typ.bit_width(),
)
}
op => operators::apply_operator(op, &transformed_value, expected_ref),
};
let display_value = string_ordering_display_value(
rule,
buffer,
absolute_offset,
max_string_length,
transformed_value,
);
Ok((matched, display_value))
}
pub(crate) fn string_ordering_display_value(
rule: &MagicRule,
buffer: &[u8],
absolute_offset: usize,
max_string_length: usize,
compared: crate::parser::ast::Value,
) -> crate::parser::ast::Value {
use crate::parser::ast::Operator::{GreaterEqual, GreaterThan, LessEqual, LessThan};
let is_ordering = matches!(rule.op, LessThan | GreaterThan | LessEqual | GreaterEqual);
if is_ordering && matches!(rule.typ, TypeKind::String { .. }) {
match types::read_string(buffer, absolute_offset, Some(max_string_length)) {
Ok(full_field) => full_field,
Err(e) => {
debug!(
"string_ordering_display_value: full-field read failed at offset {absolute_offset} for rule '{}': {e}; rendering compared prefix",
rule.message
);
compared
}
}
} else {
compared
}
}