1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, LitStr};
4
5#[proc_macro]
6pub fn parse_pattern(input: TokenStream) -> TokenStream {
7 let pattern = parse_macro_input!(input as LitStr).value().replace(" ", "");
8
9 if pattern.is_empty() {
10 panic!("Pattern cannot be empty.");
11 }
12
13 let mut bytes = Vec::new();
14 let mut masks = Vec::new();
15 let mut shift_table = vec![pattern.len(); 256]; let mut current_pos = 0;
18 let mut best_skip_value = 0u8;
19 let mut best_skip_mask = 0xFFu8;
20 let mut max_skip = 1usize;
21 let mut best_skip_offset = 0usize;
22
23 let mut chars = pattern.chars().peekable();
25 while let Some(ch) = chars.next() {
26 let next_ch = chars.peek().cloned();
27
28 match (ch, next_ch) {
29 ('?', Some('?')) => {
31 bytes.push(0x00); masks.push(0x00); chars.next(); }
35 ('?', Some(c)) if c.is_ascii_hexdigit() => {
37 let byte = u8::from_str_radix(&c.to_string(), 16).expect("Invalid nibble");
38 bytes.push(byte);
39 masks.push(0x0F); chars.next(); }
42 (c, Some('?')) if c.is_ascii_hexdigit() => {
44 let byte = u8::from_str_radix(&c.to_string(), 16).expect("Invalid nibble");
45 bytes.push(byte << 4); masks.push(0xF0); chars.next(); }
49 (c1, Some(c2)) if c1.is_ascii_hexdigit() && c2.is_ascii_hexdigit() => {
51 let byte_str = format!("{}{}", c1, c2);
52 let byte = u8::from_str_radix(&byte_str, 16).expect("Invalid hex byte");
53 bytes.push(byte);
54 masks.push(0xFF); chars.next(); }
57 _ => {
58 panic!("Invalid pattern token: {}", ch);
59 }
60 }
61
62 if masks.last() != Some(&0x00) {
64 if let Some(last_byte) = bytes.last() {
65 let skip_value = current_pos + 1;
66
67 if *masks.last().unwrap() == 0xFF {
69 if skip_value > max_skip {
70 max_skip = skip_value;
71 best_skip_offset = current_pos; best_skip_value = *last_byte; best_skip_mask = *masks.last().unwrap(); }
75 } else if *masks.last().unwrap() == 0xF0 || *masks.last().unwrap() == 0x0F {
76 if !best_skip_value == 0 && skip_value > max_skip {
78 max_skip = skip_value;
79 best_skip_offset = current_pos; best_skip_value = *last_byte; best_skip_mask = *masks.last().unwrap(); }
83 }
84 }
85 }
86
87 current_pos += 1;
88 }
89
90 let expanded = quote! {
92 Pattern {
93 bytes: [#(#bytes),*], masks: [#(#masks),*], best_skip_value: #best_skip_value, best_skip_mask: #best_skip_mask, max_skip: #max_skip, best_skip_offset: #best_skip_offset, }
100 };
101
102 TokenStream::from(expanded)
103}