use matchkit::Match;
use vyre::bytecode::{Instruction, Opcode, Program};
use vyre::gpu::gpu_pipeline::{cached_device, execute_gpu, GpuEvaluationPlan};
use vyre::FileContext;
fn run_program(
instructions: Vec<Instruction>,
file_bytes: &[u8],
matches: &[Match],
rule_string_count: usize,
) -> Option<bool> {
let (device, queue) = cached_device().ok()?;
let pattern_to_rules = (0..rule_string_count as u32)
.map(|idx| [idx, 1])
.collect::<Vec<_>>();
let rule_list = vec![0u32; rule_string_count];
let string_local_ids = (0..rule_string_count as u32).collect::<Vec<_>>();
let plan = GpuEvaluationPlan {
rule_string_counts: &[rule_string_count],
pattern_to_rules: &pattern_to_rules,
rule_list: &rule_list,
string_local_ids: &string_local_ids,
sentinel_pattern_ids: &[],
max_cached_positions: 16,
};
let program = Program { instructions };
let hits = execute_gpu(
device,
queue,
&[program],
plan,
matches,
file_bytes,
FileContext::from_bytes(file_bytes),
)
.expect("execute gpu");
Some(hits[0])
}
fn expect_bool(
instructions: Vec<Instruction>,
file_bytes: &[u8],
matches: &[Match],
rule_string_count: usize,
expected: bool,
) {
let Some(got) = run_program(instructions, file_bytes, matches, rule_string_count) else {
return;
};
assert_eq!(got, expected);
}
fn expect_eq(
mut instructions: Vec<Instruction>,
expected_value: u32,
file_bytes: &[u8],
matches: &[Match],
rule_string_count: usize,
) {
instructions.extend([
Instruction::new(Opcode::PushImmediate, expected_value),
Instruction::new(Opcode::Eq, 0),
Instruction::new(Opcode::Halt, 0),
]);
expect_bool(instructions, file_bytes, matches, rule_string_count, true);
}
fn match_row(pattern_id: u32, start: u32, end: u32) -> Match {
Match {
pattern_id,
start,
end,
padding: 0,
}
}
fn fnv1a(bytes: &[u8]) -> u32 {
let mut hash = 2_166_136_261u32;
for byte in bytes {
hash ^= *byte as u32;
hash = hash.wrapping_mul(16_777_619u32);
}
hash
}
#[test]
fn validates_new_opcodes_reject_non_zero_operands() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::MatchOrder, 1),
Instruction::new(Opcode::Halt, 0),
],
};
assert!(program.validate().is_err());
}
#[test]
fn opcode_match_order() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchOrder, 0),
Instruction::new(Opcode::Halt, 0),
],
b"abcdefghij",
&[match_row(0, 1, 3), match_row(1, 6, 8)],
2,
true,
);
}
#[test]
fn opcode_match_order_same_position() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchOrder, 0),
Instruction::new(Opcode::Halt, 0),
],
b"abcdefghij",
&[match_row(0, 5, 7), match_row(1, 5, 7)],
2,
false,
);
}
#[test]
fn opcode_match_order_no_matches() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchOrder, 0),
Instruction::new(Opcode::Halt, 0),
],
b"abcdefghij",
&[],
2,
false,
);
}
#[test]
fn opcode_match_distance() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchDistance, 0),
],
3,
b"0123456789012345",
&[match_row(0, 1, 4), match_row(1, 7, 10)],
2,
);
}
#[test]
fn opcode_match_distance_no_matches() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchDistance, 0),
],
0,
b"0123456789012345",
&[],
2,
);
}
#[test]
fn opcode_match_between() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::MatchBetween, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789012345",
&[match_row(0, 1, 2), match_row(1, 10, 11), match_row(2, 5, 6)],
3,
true,
);
}
#[test]
fn opcode_match_between_no_matches() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::MatchBetween, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789012345",
&[],
3,
false,
);
}
#[test]
fn opcode_match_between_same_position() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::MatchBetween, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789012345",
&[match_row(0, 5, 6), match_row(1, 5, 6), match_row(2, 5, 6)],
3,
true,
);
}
#[test]
fn opcode_match_same_scope() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchSameScope, 0),
Instruction::new(Opcode::Halt, 0),
],
b"{aa bb { cc }}",
&[match_row(0, 1, 3), match_row(1, 4, 6), match_row(2, 9, 11)],
3,
true,
);
}
#[test]
fn opcode_match_same_scope_no_matches() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchSameScope, 0),
Instruction::new(Opcode::Halt, 0),
],
b"{aa bb { cc }}",
&[],
3,
false,
);
}
#[test]
fn opcode_match_after() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::MatchAfter, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789",
&[match_row(0, 1, 3), match_row(1, 5, 7)],
2,
true,
);
}
#[test]
fn opcode_match_after_no_matches() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::MatchAfter, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789",
&[],
2,
false,
);
}
#[test]
fn opcode_match_after_same_position() {
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::MatchAfter, 0),
Instruction::new(Opcode::Halt, 0),
],
b"0123456789",
&[match_row(0, 5, 5), match_row(1, 5, 7)],
2,
true,
);
}
#[test]
fn opcode_matched_region_entropy() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::MatchedRegionEntropy, 0),
],
0,
b"AAAAzzzz",
&[match_row(0, 0, 4)],
1,
);
}
#[test]
fn opcode_matched_region_length() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::MatchedRegionLength, 0),
],
3,
b"abcdefghij",
&[match_row(0, 0, 2), match_row(0, 4, 7)],
1,
);
}
#[test]
fn opcode_matched_region_length_oob() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 99),
Instruction::new(Opcode::MatchedRegionLength, 0),
],
0,
b"abcdefghij",
&[match_row(0, 0, 2)],
1,
);
}
#[test]
fn opcode_pattern_density() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 6),
Instruction::new(Opcode::PatternDensity, 0),
],
3,
b"01234567890123456789012345",
&[
match_row(0, 0, 1),
match_row(0, 3, 4),
match_row(0, 5, 6),
match_row(0, 20, 21),
],
1,
);
}
#[test]
fn opcode_pattern_density_unsorted() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0),
Instruction::new(Opcode::PushImmediate, 10),
Instruction::new(Opcode::PatternDensity, 0),
],
2,
b"01234567890123456789012345",
&[
match_row(0, 0, 1),
match_row(0, 20, 21),
match_row(0, 5, 6),
],
1,
);
}
#[test]
fn opcode_unique_pattern_count() {
expect_eq(
vec![Instruction::new(Opcode::UniquePatternCount, 0)],
2,
b"0123456789",
&[match_row(0, 1, 2), match_row(0, 4, 5), match_row(1, 6, 7)],
2,
);
}
#[test]
fn opcode_total_match_count() {
expect_eq(
vec![Instruction::new(Opcode::TotalMatchCount, 0)],
3,
b"0123456789",
&[match_row(0, 1, 2), match_row(0, 4, 5), match_row(1, 6, 7)],
2,
);
}
#[test]
fn opcode_read_byte_at() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::ReadByteAt, 0),
],
0x20,
&[0x10, 0x20, 0x30],
&[],
0,
);
}
#[test]
fn opcode_read_byte_at_oob() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 100),
Instruction::new(Opcode::ReadByteAt, 0),
],
0,
&[0x10, 0x20, 0x30],
&[],
0,
);
}
#[test]
fn opcode_read_bytes_equal() {
let bytes = b"abcdef";
expect_bool(
vec![
Instruction::new(Opcode::PushImmediate, 1),
Instruction::new(Opcode::PushImmediate, 3),
Instruction::new(Opcode::PushImmediate, fnv1a(&bytes[1..4])),
Instruction::new(Opcode::ReadBytesEqual, 0),
Instruction::new(Opcode::Halt, 0),
],
bytes,
&[],
0,
true,
);
}
#[test]
fn opcode_byte_at_alias() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 2),
Instruction::new(Opcode::ByteAt, 0),
],
0x30,
&[0x10, 0x20, 0x30],
&[],
0,
);
}
#[test]
fn opcode_byte_at_oob() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 100),
Instruction::new(Opcode::ByteAt, 0),
],
0,
&[0x10, 0x20, 0x30],
&[],
0,
);
}
#[test]
fn opcode_xor_byte() {
expect_eq(
vec![
Instruction::new(Opcode::PushImmediate, 0xAA),
Instruction::new(Opcode::PushImmediate, 0x0F),
Instruction::new(Opcode::XorByte, 0),
],
0xA5,
b"",
&[],
0,
);
}