1use std::io::{self, IoSlice, Write};
2
3const IOV_BATCH: usize = 1024;
5
6fn write_all_slices(out: &mut impl Write, slices: &[IoSlice<'_>]) -> io::Result<()> {
9 if slices.len() <= 4 {
11 for s in slices {
12 out.write_all(s)?;
13 }
14 return Ok(());
15 }
16
17 let mut offset = 0;
18 while offset < slices.len() {
19 let end = (offset + IOV_BATCH).min(slices.len());
20 let n = out.write_vectored(&slices[offset..end])?;
21 if n == 0 {
22 return Err(io::Error::new(
23 io::ErrorKind::WriteZero,
24 "failed to write any data",
25 ));
26 }
27 let mut remaining = n;
28 while offset < end && remaining >= slices[offset].len() {
29 remaining -= slices[offset].len();
30 offset += 1;
31 }
32 if remaining > 0 && offset < end {
33 out.write_all(&slices[offset][remaining..])?;
34 offset += 1;
35 }
36 }
37 Ok(())
38}
39
40pub fn tac_bytes(data: &[u8], separator: u8, before: bool, out: &mut impl Write) -> io::Result<()> {
46 if data.is_empty() {
47 return Ok(());
48 }
49
50 if data.len() < 256 * 1024 {
53 return tac_bytes_small(data, separator, before, out);
54 }
55
56 let mut batch: Vec<IoSlice<'_>> = Vec::with_capacity(IOV_BATCH);
57
58 if !before {
59 let mut iter = memchr::memrchr_iter(separator, data);
61
62 let first_sep = match iter.next() {
63 Some(pos) => pos,
64 None => return out.write_all(data), };
66
67 if first_sep + 1 < data.len() {
69 batch.push(IoSlice::new(&data[first_sep + 1..]));
70 }
71
72 let mut end = first_sep + 1; for pos in iter {
76 batch.push(IoSlice::new(&data[pos + 1..end]));
77 end = pos + 1;
78 if batch.len() == IOV_BATCH {
79 write_all_slices(out, &batch)?;
80 batch.clear();
81 }
82 }
83
84 if end > 0 {
86 batch.push(IoSlice::new(&data[0..end]));
87 }
88 } else {
89 let mut end = data.len();
91
92 for pos in memchr::memrchr_iter(separator, data) {
93 batch.push(IoSlice::new(&data[pos..end]));
94 end = pos;
95 if batch.len() == IOV_BATCH {
96 write_all_slices(out, &batch)?;
97 batch.clear();
98 }
99 }
100
101 if end > 0 {
103 batch.push(IoSlice::new(&data[0..end]));
104 }
105 }
106
107 if !batch.is_empty() {
108 write_all_slices(out, &batch)?;
109 }
110 Ok(())
111}
112
113fn tac_bytes_small(
118 data: &[u8],
119 separator: u8,
120 before: bool,
121 out: &mut impl Write,
122) -> io::Result<()> {
123 let mut outbuf = vec![0u8; data.len()];
124 let mut wp = 0;
125
126 if !before {
127 let mut iter = memchr::memrchr_iter(separator, data);
129
130 let first_sep = match iter.next() {
131 Some(pos) => pos,
132 None => return out.write_all(data),
133 };
134
135 if first_sep + 1 < data.len() {
137 let trail = &data[first_sep + 1..];
138 outbuf[wp..wp + trail.len()].copy_from_slice(trail);
139 wp += trail.len();
140 }
141
142 let mut end = first_sep + 1;
143
144 for pos in iter {
145 let record = &data[pos + 1..end];
146 outbuf[wp..wp + record.len()].copy_from_slice(record);
147 wp += record.len();
148 end = pos + 1;
149 }
150
151 if end > 0 {
153 outbuf[wp..wp + end].copy_from_slice(&data[0..end]);
154 wp += end;
155 }
156 } else {
157 let mut end = data.len();
159
160 for pos in memchr::memrchr_iter(separator, data) {
161 let record = &data[pos..end];
162 outbuf[wp..wp + record.len()].copy_from_slice(record);
163 wp += record.len();
164 end = pos;
165 }
166
167 if end > 0 {
169 outbuf[wp..wp + end].copy_from_slice(&data[0..end]);
170 wp += end;
171 }
172 }
173
174 out.write_all(&outbuf[..wp])
175}
176
177pub fn tac_string_separator(
180 data: &[u8],
181 separator: &[u8],
182 before: bool,
183 out: &mut impl Write,
184) -> io::Result<()> {
185 if data.is_empty() {
186 return Ok(());
187 }
188
189 if separator.len() == 1 {
190 return tac_bytes(data, separator[0], before, out);
191 }
192
193 let estimated = (data.len() / separator.len().max(40)).max(64);
195 let mut positions: Vec<usize> = Vec::with_capacity(estimated);
196 for pos in memchr::memmem::find_iter(data, separator) {
197 positions.push(pos);
198 }
199
200 if positions.is_empty() {
201 out.write_all(data)?;
202 return Ok(());
203 }
204
205 let sep_len = separator.len();
206
207 if data.len() < 2 * 1024 * 1024 {
209 let mut outbuf = vec![0u8; data.len()];
210 let mut wp = 0;
211
212 if !before {
213 let last_end = positions.last().unwrap() + sep_len;
214
215 if last_end < data.len() {
216 let trail = &data[last_end..];
217 outbuf[wp..wp + trail.len()].copy_from_slice(trail);
218 wp += trail.len();
219 }
220
221 let mut i = positions.len();
222 while i > 0 {
223 i -= 1;
224 let sep_start = positions[i];
225 let rec_start = if i == 0 {
226 0
227 } else {
228 positions[i - 1] + sep_len
229 };
230 let record = &data[rec_start..sep_start + sep_len];
231 outbuf[wp..wp + record.len()].copy_from_slice(record);
232 wp += record.len();
233 }
234 } else {
235 let mut i = positions.len();
236 while i > 0 {
237 i -= 1;
238 let start = positions[i];
239 let end = if i + 1 < positions.len() {
240 positions[i + 1]
241 } else {
242 data.len()
243 };
244 let record = &data[start..end];
245 outbuf[wp..wp + record.len()].copy_from_slice(record);
246 wp += record.len();
247 }
248 if positions[0] > 0 {
249 outbuf[wp..wp + positions[0]].copy_from_slice(&data[..positions[0]]);
250 wp += positions[0];
251 }
252 }
253 return out.write_all(&outbuf[..wp]);
254 }
255
256 let mut batch: Vec<IoSlice<'_>> = Vec::with_capacity(IOV_BATCH);
258
259 if !before {
260 let last_end = positions.last().unwrap() + sep_len;
261 let has_trailing_sep = last_end == data.len();
262
263 if !has_trailing_sep {
264 batch.push(IoSlice::new(&data[last_end..]));
265 }
266
267 let mut i = positions.len();
268 while i > 0 {
269 i -= 1;
270 let sep_start = positions[i];
271 let rec_start = if i == 0 {
272 0
273 } else {
274 positions[i - 1] + sep_len
275 };
276 batch.push(IoSlice::new(&data[rec_start..sep_start + sep_len]));
277 if batch.len() == IOV_BATCH {
278 write_all_slices(out, &batch)?;
279 batch.clear();
280 }
281 }
282 } else {
283 let mut i = positions.len();
284 while i > 0 {
285 i -= 1;
286 let start = positions[i];
287 let end = if i + 1 < positions.len() {
288 positions[i + 1]
289 } else {
290 data.len()
291 };
292 batch.push(IoSlice::new(&data[start..end]));
293 if batch.len() == IOV_BATCH {
294 write_all_slices(out, &batch)?;
295 batch.clear();
296 }
297 }
298
299 if positions[0] > 0 {
300 batch.push(IoSlice::new(&data[..positions[0]]));
301 }
302 }
303
304 if !batch.is_empty() {
305 write_all_slices(out, &batch)?;
306 }
307
308 Ok(())
309}
310
311fn find_regex_matches_backward(data: &[u8], re: ®ex::bytes::Regex) -> Vec<(usize, usize)> {
316 let mut matches = Vec::new();
317 let mut past_end = data.len();
318
319 while past_end > 0 {
320 let buf = &data[..past_end];
321 let mut found = false;
322
323 let mut pos = past_end;
326 while pos > 0 {
327 pos -= 1;
328 if let Some(m) = re.find_at(buf, pos) {
329 if m.start() == pos {
330 matches.push((m.start(), m.end()));
332 past_end = m.start();
333 found = true;
334 break;
335 }
336 }
341 }
344
345 if !found {
346 break;
347 }
348 }
349
350 matches.reverse(); matches
352}
353
354pub fn tac_regex_separator(
359 data: &[u8],
360 pattern: &str,
361 before: bool,
362 out: &mut impl Write,
363) -> io::Result<()> {
364 if data.is_empty() {
365 return Ok(());
366 }
367
368 let re = match regex::bytes::Regex::new(pattern) {
369 Ok(r) => r,
370 Err(e) => {
371 return Err(io::Error::new(
372 io::ErrorKind::InvalidInput,
373 format!("invalid regex '{}': {}", pattern, e),
374 ));
375 }
376 };
377
378 let matches = find_regex_matches_backward(data, &re);
380
381 if matches.is_empty() {
382 out.write_all(data)?;
383 return Ok(());
384 }
385
386 if data.len() < 2 * 1024 * 1024 {
388 let mut outbuf = vec![0u8; data.len()];
389 let mut wp = 0;
390
391 if !before {
392 let last_end = matches.last().unwrap().1;
393
394 if last_end < data.len() {
395 let trail = &data[last_end..];
396 outbuf[wp..wp + trail.len()].copy_from_slice(trail);
397 wp += trail.len();
398 }
399
400 let mut i = matches.len();
401 while i > 0 {
402 i -= 1;
403 let rec_start = if i == 0 { 0 } else { matches[i - 1].1 };
404 let record = &data[rec_start..matches[i].1];
405 outbuf[wp..wp + record.len()].copy_from_slice(record);
406 wp += record.len();
407 }
408 } else {
409 let mut i = matches.len();
410 while i > 0 {
411 i -= 1;
412 let start = matches[i].0;
413 let end = if i + 1 < matches.len() {
414 matches[i + 1].0
415 } else {
416 data.len()
417 };
418 let record = &data[start..end];
419 outbuf[wp..wp + record.len()].copy_from_slice(record);
420 wp += record.len();
421 }
422 if matches[0].0 > 0 {
423 outbuf[wp..wp + matches[0].0].copy_from_slice(&data[..matches[0].0]);
424 wp += matches[0].0;
425 }
426 }
427 return out.write_all(&outbuf[..wp]);
428 }
429
430 let mut batch: Vec<IoSlice<'_>> = Vec::with_capacity(IOV_BATCH);
432
433 if !before {
434 let last_end = matches.last().unwrap().1;
435 let has_trailing_sep = last_end == data.len();
436
437 if !has_trailing_sep {
438 batch.push(IoSlice::new(&data[last_end..]));
439 }
440
441 let mut i = matches.len();
442 while i > 0 {
443 i -= 1;
444 let rec_start = if i == 0 { 0 } else { matches[i - 1].1 };
445 let rec_end = matches[i].1;
446 batch.push(IoSlice::new(&data[rec_start..rec_end]));
447 if batch.len() == IOV_BATCH {
448 write_all_slices(out, &batch)?;
449 batch.clear();
450 }
451 }
452 } else {
453 let mut i = matches.len();
454 while i > 0 {
455 i -= 1;
456 let start = matches[i].0;
457 let end = if i + 1 < matches.len() {
458 matches[i + 1].0
459 } else {
460 data.len()
461 };
462 batch.push(IoSlice::new(&data[start..end]));
463 if batch.len() == IOV_BATCH {
464 write_all_slices(out, &batch)?;
465 batch.clear();
466 }
467 }
468
469 if matches[0].0 > 0 {
470 batch.push(IoSlice::new(&data[..matches[0].0]));
471 }
472 }
473
474 if !batch.is_empty() {
475 write_all_slices(out, &batch)?;
476 }
477
478 Ok(())
479}