1use crate::vocab::InspectMode;
4
5pub struct Phrase {
7 pub mode: InspectMode,
8 pub pattern: Vec<u8>,
9 pub by: Option<Vec<u8>>,
10 pub start: usize,
12 pub end: usize,
13}
14
15pub fn region(data: &[u8], unit: usize, before: Option<&[u8]>, after: Option<&[u8]>) -> (usize, usize) {
19 let find = |needle: &[u8]| (!needle.is_empty()).then(|| (0..=data.len().saturating_sub(needle.len())).step_by(unit).find(|&p| data[p..].starts_with(needle))).flatten();
20 let start = match after {
21 Some(a) => find(a).map_or(data.len(), |p| p + a.len()),
22 None => 0,
23 };
24 let end = match before {
25 Some(b) => find(b).unwrap_or(data.len()),
26 None => data.len(),
27 };
28 (start, end.max(start))
29}
30
31pub fn inspect(data: &mut [u8], unit: usize, phrases: &[Phrase]) -> Vec<i64> {
35 let mut counts = vec![0i64; phrases.len()];
36 let mut active = vec![true; phrases.len()];
37 let mut next_leading: Vec<usize> = phrases.iter().map(|p| p.start).collect();
38 let mut at = 0;
39 while at < data.len() {
40 let mut taken = 0;
41 for (k, phrase) in phrases.iter().enumerate() {
42 if !active[k] || at < phrase.start || at >= phrase.end {
43 continue;
44 }
45 if phrase.mode == InspectMode::Leading && at != next_leading[k] {
46 active[k] = false;
47 continue;
48 }
49 let len = if phrase.mode == InspectMode::Characters { unit } else { phrase.pattern.len() };
50 let fits = len > 0 && at + len <= phrase.end;
51 let hit = fits && (phrase.mode == InspectMode::Characters || data[at..at + len] == phrase.pattern[..]);
52 if !hit {
53 if phrase.mode == InspectMode::Leading {
54 active[k] = false;
55 }
56 continue;
57 }
58 counts[k] += 1;
59 if let Some(by) = &phrase.by {
60 for (i, b) in by.iter().cycle().take(len).enumerate() {
61 data[at + i] = *b;
62 }
63 }
64 match phrase.mode {
65 InspectMode::First => active[k] = false,
66 InspectMode::Leading => next_leading[k] = at + len,
67 _ => {}
68 }
69 taken = len;
70 break;
71 }
72 at += taken.max(unit);
73 }
74 counts
75}
76
77pub fn next_delimiter(source: &[u8], from: usize, delimiters: &[(bool, Vec<u8>)], unit: usize) -> Option<(usize, usize)> {
81 (from..source.len()).step_by(unit).find_map(|p| delimiters.iter().position(|(_, d)| !d.is_empty() && source[p..].starts_with(d)).map(|k| (p, k)))
82}
83
84pub fn past_delimiter(source: &[u8], at: usize, delimiter: &[u8], all: bool) -> usize {
86 let mut end = at + delimiter.len();
87 while all && !delimiter.is_empty() && source[end..].starts_with(delimiter) {
88 end += delimiter.len();
89 }
90 end
91}
92
93pub fn delimited(bytes: &[u8], delimiter: Option<&[u8]>, unit: usize) -> Vec<u8> {
96 match delimiter {
97 Some(d) if !d.is_empty() => {
98 let end = (0..=bytes.len().saturating_sub(d.len())).step_by(unit).find(|&p| bytes[p..].starts_with(d)).unwrap_or(bytes.len());
99 bytes[..end.min(bytes.len())].to_vec()
100 }
101 _ => bytes.to_vec(),
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 fn phrase(mode: InspectMode, pattern: &str, by: Option<&str>, region: (usize, usize)) -> Phrase {
110 Phrase { mode, pattern: pattern.as_bytes().to_vec(), by: by.map(|b| b.as_bytes().to_vec()), start: region.0, end: region.1 }
111 }
112
113 #[test]
114 fn tallying_all_leading_and_characters() {
115 let mut data = b" AABA ".to_vec();
116 let n = data.len();
117 let counts = inspect(&mut data, 1, &[phrase(InspectMode::Leading, " ", None, (0, n)), phrase(InspectMode::All, "A", None, (0, n))]);
118 assert_eq!(counts, [2, 3]);
119 let counts = inspect(&mut data, 1, &[phrase(InspectMode::Characters, "", None, (0, n))]);
120 assert_eq!(counts, [8]);
121 }
122
123 #[test]
124 fn replacing_first_all_and_within_bounds() {
125 let mut data = b"A,B,C.D,E".to_vec();
126 let (start, end) = region(&data, 1, Some(b"."), None);
127 inspect(&mut data, 1, &[phrase(InspectMode::All, ",", Some(";"), (start, end))]);
128 assert_eq!(data, b"A;B;C.D,E");
129 let mut data = b"XAXAX".to_vec();
130 inspect(&mut data, 1, &[phrase(InspectMode::First, "X", Some("Y"), (0, 5))]);
131 assert_eq!(data, b"YAXAX");
132 let mut data = b"00012".to_vec();
133 inspect(&mut data, 1, &[phrase(InspectMode::Leading, "0", Some(" "), (0, 5))]);
134 assert_eq!(data, b" 12");
135 }
136
137 #[test]
138 fn after_initial_with_no_occurrence_leaves_nothing() {
139 assert_eq!(region(b"ABC", 1, None, Some(b"Z")), (3, 3));
140 assert_eq!(region(b"ABCD", 1, Some(b"D"), Some(b"A")), (1, 3));
141 }
142
143 #[test]
144 fn a_leading_run_ends_at_the_first_other_character() {
145 let mut data = b"**A**".to_vec();
146 assert_eq!(inspect(&mut data, 1, &[phrase(InspectMode::Leading, "*", None, (0, 5))]), [2]);
147 }
148
149 #[test]
150 fn national_characters_are_two_bytes() {
151 let mut data = vec![0x00, 0x41, 0x41, 0x00, 0x20, 0x00, 0x00, 0x20];
152 let n = data.len();
153 let space = Phrase { mode: InspectMode::All, pattern: vec![0x00, 0x20], by: None, start: 0, end: n };
154 let characters = Phrase { mode: InspectMode::Characters, pattern: vec![], by: None, start: 0, end: n };
155 assert_eq!(inspect(&mut data, 2, &[space, characters]), [1, 3]);
156 assert_eq!(region(&data, 2, Some(&[0x00, 0x20]), Some(&[0x00, 0x41])), (2, 6));
157 }
158
159 #[test]
160 fn delimiters() {
161 let d = vec![(true, b" ".to_vec()), (false, b",".to_vec())];
162 assert_eq!(next_delimiter(b"AB C,D", 0, &d, 1), Some((2, 0)));
163 assert_eq!(past_delimiter(b"AB C,D", 2, b" ", true), 4);
164 assert_eq!(next_delimiter(b"AB C,D", 4, &d, 1), Some((5, 1)));
165 assert_eq!(delimited(b"JOHN SMITH", Some(b" "), 1), b"JOHN");
166 }
167
168 #[test]
169 fn two_byte_characters_match_delimiters_only_at_their_own_positions() {
170 let data = [0x42, 0xC1, 0x42, 0x40, 0x42, 0xC2, 0x40, 0x40];
171 assert_eq!(delimited(&data, Some(&[0x40, 0x42]), 2), data, "X'4042' straddles two characters");
172 assert_eq!(delimited(&data, Some(&[0x40, 0x42]), 1), &data[..3]);
173 assert_eq!(delimited(&data, Some(&[0x40, 0x40]), 2), &data[..6]);
174 assert_eq!(next_delimiter(&data, 0, &[(false, vec![0x40, 0x40])], 2), Some((6, 0)));
175 }
176}