1use std::io::{self, IoSlice, Write};
2
3const MAX_IOV: usize = 1024;
6
7#[inline]
11fn flush_iov(out: &mut impl Write, slices: &[IoSlice]) -> io::Result<()> {
12 if slices.is_empty() {
13 return Ok(());
14 }
15 let total: usize = slices.iter().map(|s| s.len()).sum();
17
18 let written = match out.write_vectored(slices) {
20 Ok(n) if n >= total => return Ok(()),
21 Ok(n) => n,
22 Err(e) => return Err(e),
23 };
24
25 let mut skip = written;
27 for slice in slices {
28 let slen = slice.len();
29 if skip >= slen {
30 skip -= slen;
31 continue;
32 }
33 if skip > 0 {
34 out.write_all(&slice[skip..])?;
35 skip = 0;
36 } else {
37 out.write_all(slice)?;
38 }
39 }
40 Ok(())
41}
42
43pub fn tac_bytes(data: &[u8], separator: u8, before: bool, out: &mut impl Write) -> io::Result<()> {
47 if data.is_empty() {
48 return Ok(());
49 }
50 if !before {
51 tac_bytes_backward_after(data, separator, out)
52 } else {
53 tac_bytes_backward_before(data, separator, out)
54 }
55}
56
57fn tac_bytes_backward_after(data: &[u8], sep: u8, out: &mut impl Write) -> io::Result<()> {
64 let sep_count = memchr::memchr_iter(sep, data).count();
67 if sep_count == 0 {
68 return out.write_all(data);
69 }
70
71 let mut iov: Vec<IoSlice> = Vec::with_capacity((sep_count + 2).min(MAX_IOV));
73
74 let mut end = data.len();
75
76 let Some(mut pos) = memchr::memrchr(sep, data) else {
77 return out.write_all(data);
79 };
80
81 if pos + 1 < end {
83 iov.push(IoSlice::new(&data[pos + 1..end]));
84 }
85 end = pos + 1;
86
87 while pos > 0 {
89 match memchr::memrchr(sep, &data[..pos]) {
90 Some(prev) => {
91 iov.push(IoSlice::new(&data[prev + 1..end]));
92 if iov.len() >= MAX_IOV {
93 flush_iov(out, &iov)?;
94 iov.clear();
95 }
96 end = prev + 1;
97 pos = prev;
98 }
99 None => break,
100 }
101 }
102
103 iov.push(IoSlice::new(&data[0..end]));
105 flush_iov(out, &iov)?;
106
107 Ok(())
108}
109
110fn tac_bytes_backward_before(data: &[u8], sep: u8, out: &mut impl Write) -> io::Result<()> {
117 let sep_count = memchr::memchr_iter(sep, data).count();
119 if sep_count == 0 {
120 return out.write_all(data);
121 }
122
123 let mut iov: Vec<IoSlice> = Vec::with_capacity((sep_count + 1).min(MAX_IOV));
125
126 let mut end = data.len();
127
128 let Some(pos) = memchr::memrchr(sep, data) else {
129 return out.write_all(data);
131 };
132
133 iov.push(IoSlice::new(&data[pos..end]));
135 end = pos;
136
137 while end > 0 {
139 match memchr::memrchr(sep, &data[..end]) {
140 Some(prev) => {
141 iov.push(IoSlice::new(&data[prev..end]));
142 if iov.len() >= MAX_IOV {
143 flush_iov(out, &iov)?;
144 iov.clear();
145 }
146 end = prev;
147 }
148 None => break,
149 }
150 }
151
152 if end > 0 {
154 iov.push(IoSlice::new(&data[0..end]));
155 }
156
157 flush_iov(out, &iov)?;
158 Ok(())
159}
160
161pub fn tac_string_separator(
167 data: &[u8],
168 separator: &[u8],
169 before: bool,
170 out: &mut impl Write,
171) -> io::Result<()> {
172 if data.is_empty() {
173 return Ok(());
174 }
175
176 if separator.len() == 1 {
177 return tac_bytes(data, separator[0], before, out);
178 }
179
180 let sep_len = separator.len();
181 let finder = memchr::memmem::FinderRev::new(separator);
182
183 let sep_count = memchr::memmem::find_iter(data, separator).count();
186 if sep_count == 0 {
187 return out.write_all(data);
188 }
189
190 let mut iov: Vec<IoSlice> = Vec::with_capacity((sep_count + 2).min(MAX_IOV));
191
192 if !before {
193 let mut end = data.len();
194
195 let Some(mut pos) = finder.rfind(data) else {
196 return out.write_all(data);
198 };
199
200 if pos + sep_len < end {
202 iov.push(IoSlice::new(&data[pos + sep_len..end]));
203 }
204 end = pos + sep_len;
205
206 while pos > 0 {
208 match finder.rfind(&data[..pos]) {
209 Some(prev) => {
210 iov.push(IoSlice::new(&data[prev + sep_len..end]));
211 if iov.len() >= MAX_IOV {
212 flush_iov(out, &iov)?;
213 iov.clear();
214 }
215 end = prev + sep_len;
216 pos = prev;
217 }
218 None => break,
219 }
220 }
221
222 iov.push(IoSlice::new(&data[0..end]));
224 } else {
225 let mut end = data.len();
226
227 let Some(pos) = finder.rfind(data) else {
228 return out.write_all(data);
230 };
231
232 iov.push(IoSlice::new(&data[pos..end]));
234 end = pos;
235
236 while end > 0 {
238 match finder.rfind(&data[..end]) {
239 Some(prev) => {
240 iov.push(IoSlice::new(&data[prev..end]));
241 if iov.len() >= MAX_IOV {
242 flush_iov(out, &iov)?;
243 iov.clear();
244 }
245 end = prev;
246 }
247 None => break,
248 }
249 }
250
251 if end > 0 {
253 iov.push(IoSlice::new(&data[0..end]));
254 }
255 }
256
257 flush_iov(out, &iov)?;
258 Ok(())
259}
260
261fn find_regex_matches_backward(data: &[u8], re: ®ex::bytes::Regex) -> Vec<(usize, usize)> {
263 let mut matches = Vec::new();
264 let mut past_end = data.len();
265
266 while past_end > 0 {
267 let buf = &data[..past_end];
268 let mut found = false;
269
270 let mut pos = past_end;
271 while pos > 0 {
272 pos -= 1;
273 if let Some(m) = re.find_at(buf, pos) {
274 if m.start() == pos {
275 matches.push((m.start(), m.end()));
276 past_end = m.start();
277 found = true;
278 break;
279 }
280 }
281 }
282
283 if !found {
284 break;
285 }
286 }
287
288 matches.reverse();
289 matches
290}
291
292pub fn tac_regex_separator(
294 data: &[u8],
295 pattern: &str,
296 before: bool,
297 out: &mut impl Write,
298) -> io::Result<()> {
299 if data.is_empty() {
300 return Ok(());
301 }
302
303 let re = match regex::bytes::Regex::new(pattern) {
304 Ok(r) => r,
305 Err(e) => {
306 return Err(io::Error::new(
307 io::ErrorKind::InvalidInput,
308 format!("invalid regex '{}': {}", pattern, e),
309 ));
310 }
311 };
312
313 let matches = find_regex_matches_backward(data, &re);
314
315 if matches.is_empty() {
316 out.write_all(data)?;
317 return Ok(());
318 }
319
320 let mut iov: Vec<IoSlice> = Vec::with_capacity(matches.len().min(MAX_IOV) + 2);
321
322 if !before {
323 let last_end = matches.last().unwrap().1;
324
325 if last_end < data.len() {
326 iov.push(IoSlice::new(&data[last_end..]));
327 }
328
329 let mut i = matches.len();
330 while i > 0 {
331 i -= 1;
332 let rec_start = if i == 0 { 0 } else { matches[i - 1].1 };
333 iov.push(IoSlice::new(&data[rec_start..matches[i].1]));
334 if iov.len() >= MAX_IOV {
335 flush_iov(out, &iov)?;
336 iov.clear();
337 }
338 }
339 } else {
340 let mut i = matches.len();
341 while i > 0 {
342 i -= 1;
343 let start = matches[i].0;
344 let end = if i + 1 < matches.len() {
345 matches[i + 1].0
346 } else {
347 data.len()
348 };
349 iov.push(IoSlice::new(&data[start..end]));
350 if iov.len() >= MAX_IOV {
351 flush_iov(out, &iov)?;
352 iov.clear();
353 }
354 }
355
356 if matches[0].0 > 0 {
357 iov.push(IoSlice::new(&data[..matches[0].0]));
358 }
359 }
360
361 flush_iov(out, &iov)?;
362 Ok(())
363}