use crate::evaluator::operators::equality::apply_equal;
use crate::parser::ast::Value;
#[must_use]
pub fn apply_bitwise_and_mask(mask: u64, left: &Value, right: &Value) -> bool {
apply_bitwise_and_mask_with_width(mask, left, right, None)
}
#[must_use]
pub fn apply_bitwise_and_mask_with_width(
mask: u64,
left: &Value,
right: &Value,
bit_width: Option<u32>,
) -> bool {
let masked_left = match (left, bit_width) {
(Value::Uint(val), Some(width)) if width < 64 => {
let width_mask = (1u64 << width) - 1;
Value::Uint((val & mask) & width_mask)
}
(Value::Uint(val), _) => Value::Uint(val & mask),
(Value::Int(val), Some(width)) if width < 64 => {
let width_mask = (1u64 << width) - 1;
let masked = u64::from_ne_bytes(val.to_ne_bytes()) & mask & width_mask;
let sign_bit = 1u64 << (width - 1);
let extended = if masked & sign_bit != 0 {
masked | !width_mask
} else {
masked
};
Value::Int(i64::from_ne_bytes(extended.to_ne_bytes()))
}
(Value::Int(val), _) => {
let i64_mask =
i64::try_from(mask).unwrap_or_else(|_| i64::from_ne_bytes(mask.to_ne_bytes()));
Value::Int(val & i64_mask)
}
_ => return false, };
apply_equal(&masked_left, right)
}
#[must_use]
pub fn apply_bitwise_and(left: &Value, right: &Value) -> bool {
match (left, right) {
(Value::Uint(a), Value::Uint(b)) => (a & b) == *b,
#[allow(clippy::cast_sign_loss)]
(Value::Int(a), Value::Int(b)) => {
let (a, b) = (*a as u64, *b as u64);
(a & b) == b
}
#[allow(clippy::cast_sign_loss)]
(Value::Uint(a), Value::Int(b)) => {
let b = *b as u64;
(a & b) == b
}
#[allow(clippy::cast_sign_loss)]
(Value::Int(a), Value::Uint(b)) => ((*a as u64) & b) == *b,
_ => false,
}
}
#[must_use]
pub fn apply_bitwise_xor(left: &Value, right: &Value) -> bool {
match (left, right) {
(Value::Uint(a), Value::Uint(b)) => (a ^ b) != 0,
#[allow(clippy::cast_sign_loss)]
(Value::Int(a), Value::Int(b)) => ((*a as u64) ^ (*b as u64)) != 0,
#[allow(clippy::cast_sign_loss)]
(Value::Uint(a), Value::Int(b)) => (a ^ (*b as u64)) != 0,
#[allow(clippy::cast_sign_loss)]
(Value::Int(a), Value::Uint(b)) => ((*a as u64) ^ b) != 0,
_ => false,
}
}
#[must_use]
pub fn apply_bitwise_not(left: &Value, right: &Value) -> bool {
apply_bitwise_not_with_width(left, right, None)
}
#[must_use]
pub fn apply_bitwise_not_with_width(left: &Value, right: &Value, bit_width: Option<u32>) -> bool {
let complemented = match (left, bit_width) {
(Value::Uint(val), Some(width)) if width < 64 => {
let mask = (1u64 << width) - 1;
Value::Uint(!val & mask)
}
(Value::Uint(val), _) => Value::Uint(!val),
(Value::Int(val), _) => Value::Int(!*val),
_ => return false,
};
apply_equal(&complemented, right)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_bitwise_and_uint_basic() {
assert!(apply_bitwise_and(&Value::Uint(0x01), &Value::Uint(0x01))); assert!(!apply_bitwise_and(&Value::Uint(0x02), &Value::Uint(0x01))); assert!(apply_bitwise_and(&Value::Uint(0x03), &Value::Uint(0x01))); }
#[test]
fn test_apply_bitwise_and_uint_multiple_bits() {
assert!(apply_bitwise_and(&Value::Uint(0xFF), &Value::Uint(0x0F))); assert!(!apply_bitwise_and(&Value::Uint(0xF0), &Value::Uint(0x0F))); assert!(!apply_bitwise_and(&Value::Uint(0xAA), &Value::Uint(0x55))); assert!(apply_bitwise_and(&Value::Uint(0xAA), &Value::Uint(0xAA))); }
#[test]
fn test_apply_bitwise_and_uint_edge_cases() {
assert!(!apply_bitwise_and(&Value::Uint(0), &Value::Uint(0xFF))); assert!(apply_bitwise_and(&Value::Uint(0xFF), &Value::Uint(0))); assert!(apply_bitwise_and(&Value::Uint(0), &Value::Uint(0)));
assert!(apply_bitwise_and(&Value::Uint(u64::MAX), &Value::Uint(1))); assert!(apply_bitwise_and(
&Value::Uint(u64::MAX),
&Value::Uint(u64::MAX)
)); }
#[test]
fn test_apply_bitwise_and_uint_specific_patterns() {
assert!(!apply_bitwise_and(
&Value::Uint(0x7F45_4C46),
&Value::Uint(0xFF00_0000)
)); assert!(apply_bitwise_and(
&Value::Uint(0x7F45_4C46),
&Value::Uint(0x7F00_0000)
)); assert!(!apply_bitwise_and(
&Value::Uint(0x504B_0304),
&Value::Uint(0xFFFF_0000)
)); assert!(apply_bitwise_and(
&Value::Uint(0x504B_0304),
&Value::Uint(0x504B_0000)
)); assert!(!apply_bitwise_and(
&Value::Uint(0x1234_5678),
&Value::Uint(0x0000_0001)
)); }
#[test]
fn test_apply_bitwise_and_int_basic() {
assert!(apply_bitwise_and(&Value::Int(1), &Value::Int(1))); assert!(!apply_bitwise_and(&Value::Int(2), &Value::Int(1))); assert!(apply_bitwise_and(&Value::Int(3), &Value::Int(1))); }
#[test]
fn test_apply_bitwise_and_int_negative() {
assert!(apply_bitwise_and(&Value::Int(-1), &Value::Int(1))); assert!(apply_bitwise_and(&Value::Int(-2), &Value::Int(2))); assert!(!apply_bitwise_and(&Value::Int(-2), &Value::Int(1))); }
#[test]
fn test_apply_bitwise_and_int_zero() {
assert!(!apply_bitwise_and(&Value::Int(0), &Value::Int(0xFF))); assert!(apply_bitwise_and(&Value::Int(0xFF), &Value::Int(0))); assert!(apply_bitwise_and(&Value::Int(0), &Value::Int(0))); }
#[test]
fn test_apply_bitwise_and_int_extreme_values() {
assert!(apply_bitwise_and(&Value::Int(i64::MAX), &Value::Int(1))); assert!(apply_bitwise_and(
&Value::Int(i64::MIN),
&Value::Int(i64::MIN)
));
assert!(!apply_bitwise_and(&Value::Int(i64::MIN), &Value::Int(-1)));
assert!(apply_bitwise_and(&Value::Int(-1), &Value::Int(i64::MIN)));
}
#[test]
fn test_apply_bitwise_and_mixed_int_uint() {
assert!(apply_bitwise_and(&Value::Uint(0xFF), &Value::Int(0x0F))); assert!(apply_bitwise_and(&Value::Int(0xFF), &Value::Uint(0x0F))); assert!(!apply_bitwise_and(&Value::Uint(0xF0), &Value::Int(0x0F))); assert!(!apply_bitwise_and(&Value::Int(0xF0), &Value::Uint(0x0F))); }
#[test]
fn test_apply_bitwise_and_mixed_negative_uint() {
assert!(apply_bitwise_and(&Value::Int(-1), &Value::Uint(1)));
assert!(!apply_bitwise_and(&Value::Uint(1), &Value::Int(-1)));
assert!(!apply_bitwise_and(&Value::Int(-2), &Value::Uint(1))); assert!(!apply_bitwise_and(&Value::Uint(1), &Value::Int(-2))); }
#[test]
fn test_apply_bitwise_and_non_integer_types() {
assert!(!apply_bitwise_and(
&Value::String("test".to_string()),
&Value::Uint(0x01)
));
assert!(!apply_bitwise_and(
&Value::Uint(0x01),
&Value::String("test".to_string())
));
assert!(!apply_bitwise_and(
&Value::Bytes(vec![1]),
&Value::Uint(0x01)
));
assert!(!apply_bitwise_and(
&Value::Uint(0x01),
&Value::Bytes(vec![1])
));
assert!(!apply_bitwise_and(
&Value::String("a".to_string()),
&Value::String("b".to_string())
));
assert!(!apply_bitwise_and(
&Value::Bytes(vec![1]),
&Value::Bytes(vec![1])
));
}
#[test]
fn test_apply_bitwise_and_all_non_integer_combinations() {
let non_integer_values = [Value::String("test".to_string()), Value::Bytes(vec![42])];
let integer_values = [Value::Uint(42), Value::Int(42)];
for non_int in &non_integer_values {
for int_val in &integer_values {
assert!(
!apply_bitwise_and(non_int, int_val),
"Non-integer & integer should be false: {non_int:?} & {int_val:?}"
);
assert!(
!apply_bitwise_and(int_val, non_int),
"Integer & non-integer should be false: {int_val:?} & {non_int:?}"
);
}
}
for left in &non_integer_values {
for right in &non_integer_values {
assert!(
!apply_bitwise_and(left, right),
"Non-integer & non-integer should be false: {left:?} & {right:?}"
);
}
}
}
#[test]
fn test_apply_bitwise_and_bit_patterns() {
let test_cases = vec![
(0b0000_0001_u64, 0b0000_0001_u64, true), (0b0000_0010_u64, 0b0000_0001_u64, false), (0b0000_0011_u64, 0b0000_0001_u64, true), (0b1111_1111_u64, 0b0000_1111_u64, true), (0b1111_0000_u64, 0b0000_1111_u64, false), (0b1010_1010_u64, 0b0101_0101_u64, false), (0b1010_1010_u64, 0b1010_1010_u64, true), (0b1111_1111_u64, 0b0000_0000_u64, true), (0b0000_0000_u64, 0b1111_1111_u64, false), ];
for (value, mask, expected) in test_cases {
assert_eq!(
apply_bitwise_and(&Value::Uint(value), &Value::Uint(mask)),
expected,
"apply_bitwise_and(0b{value:08b}, 0b{mask:08b}) should be {expected}"
);
}
}
#[test]
fn test_apply_bitwise_and_magic_file_patterns() {
let elf_magic = Value::Uint(0x7F45_4C46);
let elf_all_ones_mask = Value::Uint(0xFFFF_FFFF);
assert!(!apply_bitwise_and(&elf_magic, &elf_all_ones_mask));
assert!(apply_bitwise_and(&elf_magic, &Value::Uint(0x7F00_0000))); assert!(apply_bitwise_and(&elf_magic, &Value::Uint(0x0045_0000))); assert!(apply_bitwise_and(&elf_magic, &Value::Uint(0x0000_4C00))); assert!(apply_bitwise_and(&elf_magic, &Value::Uint(0x0000_0046)));
let zip_magic = Value::Uint(0x504B_0304);
assert!(apply_bitwise_and(&zip_magic, &Value::Uint(0x504B_0000))); assert!(!apply_bitwise_and(&zip_magic, &Value::Uint(0x0000_0001)));
let pdf_magic = Value::Uint(0x2550_4446); assert!(!apply_bitwise_and(&pdf_magic, &Value::Uint(0xFF00_0000))); assert!(!apply_bitwise_and(&pdf_magic, &Value::Uint(0x00FF_0000))); }
#[test]
fn test_apply_bitwise_and_is_not_commutative_in_general() {
let asymmetric_cases = vec![
(Value::Uint(0xFF), Value::Uint(0x0F)), (Value::Uint(1), Value::Int(-1)), ];
for (left, right) in asymmetric_cases {
let left_to_right = apply_bitwise_and(&left, &right);
let right_to_left = apply_bitwise_and(&right, &left);
assert_ne!(
left_to_right, right_to_left,
"expected asymmetric result for {left:?} & {right:?} vs swapped operands"
);
}
let self_case = Value::Uint(0x5555);
assert_eq!(
apply_bitwise_and(&self_case, &self_case),
apply_bitwise_and(&self_case, &self_case)
);
assert!(apply_bitwise_and(&self_case, &self_case));
}
#[test]
fn test_apply_bitwise_and_associativity_concept() {
let value = Value::Uint(0b1111_0000);
let mask1 = Value::Uint(0b1100_0000);
let mask2 = Value::Uint(0b0011_0000);
let combined_mask = Value::Uint(0b1111_0000);
assert!(apply_bitwise_and(&value, &mask1));
assert!(apply_bitwise_and(&value, &mask2));
assert!(apply_bitwise_and(&value, &combined_mask));
}
#[test]
fn test_apply_bitwise_xor_uint() {
assert!(apply_bitwise_xor(&Value::Uint(0xFF), &Value::Uint(0x0F)));
assert!(!apply_bitwise_xor(&Value::Uint(0xFF), &Value::Uint(0xFF)));
assert!(apply_bitwise_xor(&Value::Uint(1), &Value::Uint(2)));
assert!(!apply_bitwise_xor(&Value::Uint(0), &Value::Uint(0)));
}
#[test]
fn test_apply_bitwise_xor_int() {
assert!(apply_bitwise_xor(&Value::Int(0xFF), &Value::Int(0x0F)));
assert!(!apply_bitwise_xor(&Value::Int(42), &Value::Int(42)));
assert!(apply_bitwise_xor(&Value::Int(-1), &Value::Int(0)));
}
#[test]
fn test_apply_bitwise_xor_cross_type() {
assert!(apply_bitwise_xor(&Value::Uint(0xFF), &Value::Int(0x0F)));
assert!(apply_bitwise_xor(&Value::Int(0xFF), &Value::Uint(0x0F)));
assert!(!apply_bitwise_xor(&Value::Uint(42), &Value::Int(42)));
}
#[test]
fn test_apply_bitwise_xor_same_value() {
assert!(!apply_bitwise_xor(&Value::Uint(100), &Value::Uint(100)));
assert!(!apply_bitwise_xor(&Value::Int(-1), &Value::Int(-1)));
}
#[test]
fn test_apply_bitwise_xor_non_numeric() {
assert!(!apply_bitwise_xor(
&Value::Bytes(vec![1, 2]),
&Value::Uint(1)
));
assert!(!apply_bitwise_xor(
&Value::String("x".to_string()),
&Value::Uint(0xFF)
));
}
#[test]
fn test_apply_bitwise_not_uint() {
assert!(apply_bitwise_not(&Value::Uint(0), &Value::Uint(u64::MAX)));
assert!(apply_bitwise_not(&Value::Uint(u64::MAX), &Value::Uint(0)));
assert!(!apply_bitwise_not(&Value::Uint(0xFF), &Value::Uint(0)));
}
#[test]
fn test_apply_bitwise_not_int() {
assert!(apply_bitwise_not(&Value::Int(0), &Value::Int(-1)));
assert!(apply_bitwise_not(&Value::Int(-1), &Value::Int(0)));
}
#[test]
fn test_apply_bitwise_not_all_bits_set() {
assert!(apply_bitwise_not(
&Value::Uint(0xFFFF_FFFF_FFFF_FFFF),
&Value::Uint(0)
));
}
#[test]
fn test_apply_bitwise_not_non_numeric() {
assert!(!apply_bitwise_not(
&Value::Bytes(vec![0xff]),
&Value::Uint(0)
));
assert!(!apply_bitwise_not(
&Value::String("x".to_string()),
&Value::Uint(0)
));
}
#[test]
fn test_apply_bitwise_not_with_byte_width() {
assert!(apply_bitwise_not_with_width(
&Value::Uint(0x00),
&Value::Uint(0xFF),
Some(8)
));
assert!(apply_bitwise_not_with_width(
&Value::Uint(0xFF),
&Value::Uint(0x00),
Some(8)
));
assert!(apply_bitwise_not_with_width(
&Value::Uint(0x42),
&Value::Uint(0xBD),
Some(8)
));
}
#[test]
fn test_apply_bitwise_not_with_short_width() {
assert!(apply_bitwise_not_with_width(
&Value::Uint(0x0000),
&Value::Uint(0xFFFF),
Some(16)
));
assert!(apply_bitwise_not_with_width(
&Value::Uint(0x1234),
&Value::Uint(0xEDCB),
Some(16)
));
}
#[test]
fn test_apply_bitwise_not_with_long_width() {
assert!(apply_bitwise_not_with_width(
&Value::Uint(0x0000_0000),
&Value::Uint(0xFFFF_FFFF),
Some(32)
));
}
#[test]
fn test_apply_bitwise_not_with_quad_width() {
assert!(apply_bitwise_not_with_width(
&Value::Uint(0),
&Value::Uint(u64::MAX),
Some(64)
));
}
#[test]
fn test_apply_bitwise_not_with_no_width() {
assert!(apply_bitwise_not_with_width(
&Value::Uint(0),
&Value::Uint(u64::MAX),
None
));
}
#[test]
#[allow(clippy::cast_possible_wrap)] fn test_apply_bitwise_and_mask_with_width_matches_signed_macho_signature() {
let read = Value::Int(0xFFFF_FFFF_FEED_FACF_u64 as i64);
let rule = Value::Int(0xFFFF_FFFF_FEED_FACE_u64 as i64);
assert!(
apply_bitwise_and_mask_with_width(0xffff_fffe, &read, &rule, Some(32)),
"signed lelong&0xfffffffe must equal sign-extended 0xfeedface at 32-bit width"
);
assert!(
!apply_bitwise_and_mask_with_width(0xffff_fffe, &read, &rule, None),
"without the type width the sign-extension mismatch resurfaces (the bug)"
);
}
#[test]
#[allow(clippy::cast_possible_wrap)] fn test_apply_bitwise_and_mask_with_width_signed_negative_case() {
let read = Value::Int(0x0000_0000_1234_5678_u64 as i64); let rule = Value::Int(0xFFFF_FFFF_FEED_FACE_u64 as i64); assert!(!apply_bitwise_and_mask_with_width(
0xffff_fffe,
&read,
&rule,
Some(32)
));
}
#[test]
fn test_apply_bitwise_and_mask_with_width_unsigned_unaffected() {
let read = Value::Uint(0x0000_0000_FEED_FACF); let rule = Value::Uint(0x0000_0000_FEED_FACE); assert!(apply_bitwise_and_mask_with_width(
0xffff_fffe,
&read,
&rule,
Some(32)
));
assert!(apply_bitwise_and_mask_with_width(
0xffff_fffe,
&read,
&rule,
None
));
}
#[test]
#[allow(clippy::cast_possible_wrap)] fn test_apply_bitwise_and_mask_with_width_signed_byte() {
let read = Value::Int(0xFFFF_FFFF_FFFF_FF81_u64 as i64);
let rule = Value::Int(0xFFFF_FFFF_FFFF_FF80_u64 as i64); assert!(apply_bitwise_and_mask_with_width(
0xfe,
&read,
&rule,
Some(8)
));
}
#[test]
fn test_apply_bitwise_and_mask_delegates_to_none_width() {
let read = Value::Uint(0x1234);
let rule = Value::Uint(0x34);
assert_eq!(
apply_bitwise_and_mask(0xFF, &read, &rule),
apply_bitwise_and_mask_with_width(0xFF, &read, &rule, None)
);
}
}