-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathc_macro.rs
101 lines (88 loc) · 2.76 KB
/
c_macro.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::collections::HashSet;
use crate::c_langs_macros::is_predefined_macros;
const DOLLARS: [u8; 2048] = [b'$'; 2048];
#[inline(always)]
fn is_identifier_part(c: u8) -> bool {
c.is_ascii_uppercase() || c.is_ascii_lowercase() || c.is_ascii_digit() || c == b'_'
}
#[inline(always)]
fn is_identifier_starter(c: u8) -> bool {
c.is_ascii_uppercase() || c.is_ascii_lowercase() || c == b'_'
}
#[inline(always)]
fn is_macro<S: ::std::hash::BuildHasher>(mac: &str, macros: &HashSet<String, S>) -> bool {
macros.contains(mac) | is_predefined_macros(mac)
}
pub fn replace<S: ::std::hash::BuildHasher>(
code: &[u8],
macros: &HashSet<String, S>,
) -> Option<Vec<u8>> {
let mut new_code = Vec::with_capacity(code.len());
let mut code_start = 0;
let mut k_start = 0;
for (i, c) in code.iter().enumerate() {
if k_start != 0 {
if !is_identifier_part(*c) {
let start = k_start - 1;
k_start = 0;
let keyword = String::from_utf8(code[start..i].to_vec()).unwrap();
if is_macro(&keyword, macros) {
new_code.extend(&code[code_start..start]);
new_code.extend(&DOLLARS[..(i - start)]);
code_start = i;
}
}
} else if is_identifier_starter(*c) {
k_start = i + 1;
}
}
if k_start != 0 {
let start = k_start - 1;
let i = code.len();
let keyword = String::from_utf8(code[start..].to_vec()).unwrap();
if is_macro(&keyword, macros) {
new_code.extend(&code[code_start..start]);
new_code.extend(&DOLLARS[..(i - start)]);
code_start = i;
}
}
if code_start == 0 {
None
} else {
if code_start < code.len() {
new_code.extend(&code[code_start..]);
}
Some(new_code)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_replace() {
let mut mac = HashSet::new();
mac.insert("abc".to_string());
assert!(replace(b"def ghi jkl", &mac).is_none());
assert_eq!(
b"$$$ def ghi jkl".to_vec(),
replace(b"abc def ghi jkl", &mac).unwrap()
);
assert_eq!(
b"def $$$ ghi jkl".to_vec(),
replace(b"def abc ghi jkl", &mac).unwrap()
);
assert_eq!(
b"def ghi $$$ jkl".to_vec(),
replace(b"def ghi abc jkl", &mac).unwrap()
);
assert_eq!(
b"def ghi jkl $$$".to_vec(),
replace(b"def ghi jkl abc", &mac).unwrap()
);
mac.insert("z9_".to_string());
assert_eq!(
b"$$$ def ghi $$$ jkl".to_vec(),
replace(b"abc def ghi z9_ jkl", &mac).unwrap()
);
}
}