1use std::io::{self, IoSlice, Write};
2
3const IOV_BATCH: usize = 1024;
5
6const VECTORED_MAX_RECORDS: usize = 256 * 1024;
9
10fn write_all_slices(out: &mut impl Write, slices: &[IoSlice<'_>]) -> io::Result<()> {
13 let mut offset = 0;
14 while offset < slices.len() {
15 let end = (offset + IOV_BATCH).min(slices.len());
16 let n = out.write_vectored(&slices[offset..end])?;
17 if n == 0 {
18 return Err(io::Error::new(
19 io::ErrorKind::WriteZero,
20 "failed to write any data",
21 ));
22 }
23 let mut remaining = n;
25 while offset < end && remaining >= slices[offset].len() {
26 remaining -= slices[offset].len();
27 offset += 1;
28 }
29 if remaining > 0 && offset < end {
31 out.write_all(&slices[offset][remaining..])?;
32 offset += 1;
33 }
34 }
35 Ok(())
36}
37
38const SIMPLE_THRESHOLD: usize = 100;
44
45pub 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 let positions: Vec<usize> = memchr::memchr_iter(separator, data).collect();
52
53 if positions.is_empty() {
54 out.write_all(data)?;
55 return Ok(());
56 }
57
58 if positions.len() <= SIMPLE_THRESHOLD {
60 return tac_bytes_simple(data, separator, before, &positions, out);
61 }
62
63 if positions.len() > VECTORED_MAX_RECORDS {
66 return tac_bytes_bufwriter(data, separator, before, &positions, out);
67 }
68
69 if !before {
71 let has_trailing_sep = *positions.last().unwrap() == data.len() - 1;
72 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(positions.len() + 4);
73
74 if !has_trailing_sep {
76 let last_sep = *positions.last().unwrap();
77 slices.push(IoSlice::new(&data[last_sep + 1..]));
78 }
79
80 let mut i = positions.len();
81 while i > 0 {
82 i -= 1;
83 let end = positions[i] + 1;
84 let start = if i == 0 { 0 } else { positions[i - 1] + 1 };
85 slices.push(IoSlice::new(&data[start..end]));
86 }
87
88 write_all_slices(out, &slices)?;
89 } else {
90 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(positions.len() + 2);
91
92 let mut i = positions.len();
93 while i > 0 {
94 i -= 1;
95 let start = positions[i];
96 let end = if i + 1 < positions.len() {
97 positions[i + 1]
98 } else {
99 data.len()
100 };
101 slices.push(IoSlice::new(&data[start..end]));
102 }
103
104 if positions[0] > 0 {
105 slices.push(IoSlice::new(&data[..positions[0]]));
106 }
107
108 write_all_slices(out, &slices)?;
109 }
110
111 Ok(())
112}
113
114fn tac_bytes_simple(
117 data: &[u8],
118 _separator: u8,
119 before: bool,
120 positions: &[usize],
121 out: &mut impl Write,
122) -> io::Result<()> {
123 if !before {
124 let has_trailing_sep = *positions.last().unwrap() == data.len() - 1;
125
126 if !has_trailing_sep {
127 let last_sep = *positions.last().unwrap();
128 out.write_all(&data[last_sep + 1..])?;
129 }
130
131 let mut i = positions.len();
132 while i > 0 {
133 i -= 1;
134 let end = positions[i] + 1;
135 let start = if i == 0 { 0 } else { positions[i - 1] + 1 };
136 out.write_all(&data[start..end])?;
137 }
138 } else {
139 let mut i = positions.len();
140 while i > 0 {
141 i -= 1;
142 let start = positions[i];
143 let end = if i + 1 < positions.len() {
144 positions[i + 1]
145 } else {
146 data.len()
147 };
148 out.write_all(&data[start..end])?;
149 }
150 if positions[0] > 0 {
151 out.write_all(&data[..positions[0]])?;
152 }
153 }
154 Ok(())
155}
156
157fn tac_bytes_bufwriter(
159 data: &[u8],
160 _separator: u8,
161 before: bool,
162 positions: &[usize],
163 out: &mut impl Write,
164) -> io::Result<()> {
165 let mut buf = io::BufWriter::with_capacity(4 * 1024 * 1024, out);
166
167 if !before {
168 let has_trailing_sep = *positions.last().unwrap() == data.len() - 1;
169 if !has_trailing_sep {
170 let last_sep = *positions.last().unwrap();
171 buf.write_all(&data[last_sep + 1..])?;
172 }
173 let mut i = positions.len();
174 while i > 0 {
175 i -= 1;
176 let end = positions[i] + 1;
177 let start = if i == 0 { 0 } else { positions[i - 1] + 1 };
178 buf.write_all(&data[start..end])?;
179 }
180 } else {
181 let mut i = positions.len();
182 while i > 0 {
183 i -= 1;
184 let start = positions[i];
185 let end = if i + 1 < positions.len() {
186 positions[i + 1]
187 } else {
188 data.len()
189 };
190 buf.write_all(&data[start..end])?;
191 }
192 if positions[0] > 0 {
193 buf.write_all(&data[..positions[0]])?;
194 }
195 }
196 buf.flush()?;
197 Ok(())
198}
199
200pub fn tac_string_separator(
203 data: &[u8],
204 separator: &[u8],
205 before: bool,
206 out: &mut impl Write,
207) -> io::Result<()> {
208 if data.is_empty() {
209 return Ok(());
210 }
211
212 if separator.len() == 1 {
213 return tac_bytes(data, separator[0], before, out);
214 }
215
216 let positions: Vec<usize> = memchr::memmem::find_iter(data, separator).collect();
218
219 if positions.is_empty() {
220 out.write_all(data)?;
221 return Ok(());
222 }
223
224 let sep_len = separator.len();
225
226 if !before {
227 let last_end = positions.last().unwrap() + sep_len;
228 let has_trailing_sep = last_end == data.len();
229 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(positions.len() + 4);
230
231 if !has_trailing_sep {
233 slices.push(IoSlice::new(&data[last_end..]));
234 }
235
236 let mut i = positions.len();
237 while i > 0 {
238 i -= 1;
239 let sep_start = positions[i];
240 let rec_start = if i == 0 {
241 0
242 } else {
243 positions[i - 1] + sep_len
244 };
245 slices.push(IoSlice::new(&data[rec_start..sep_start + sep_len]));
246 }
247
248 write_all_slices(out, &slices)?;
249 } else {
250 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(positions.len() + 2);
251
252 let mut i = positions.len();
253 while i > 0 {
254 i -= 1;
255 let start = positions[i];
256 let end = if i + 1 < positions.len() {
257 positions[i + 1]
258 } else {
259 data.len()
260 };
261 slices.push(IoSlice::new(&data[start..end]));
262 }
263
264 if positions[0] > 0 {
265 slices.push(IoSlice::new(&data[..positions[0]]));
266 }
267
268 write_all_slices(out, &slices)?;
269 }
270
271 Ok(())
272}
273
274fn find_regex_matches_backward(data: &[u8], re: ®ex::bytes::Regex) -> Vec<(usize, usize)> {
279 let mut matches = Vec::new();
280 let mut past_end = data.len();
281
282 while past_end > 0 {
283 let buf = &data[..past_end];
284 let mut found = false;
285
286 let mut pos = past_end;
289 while pos > 0 {
290 pos -= 1;
291 if let Some(m) = re.find_at(buf, pos) {
292 if m.start() == pos {
293 matches.push((m.start(), m.end()));
295 past_end = m.start();
296 found = true;
297 break;
298 }
299 }
304 }
307
308 if !found {
309 break;
310 }
311 }
312
313 matches.reverse(); matches
315}
316
317pub fn tac_regex_separator(
322 data: &[u8],
323 pattern: &str,
324 before: bool,
325 out: &mut impl Write,
326) -> io::Result<()> {
327 if data.is_empty() {
328 return Ok(());
329 }
330
331 let re = match regex::bytes::Regex::new(pattern) {
332 Ok(r) => r,
333 Err(e) => {
334 return Err(io::Error::new(
335 io::ErrorKind::InvalidInput,
336 format!("invalid regex '{}': {}", pattern, e),
337 ));
338 }
339 };
340
341 let matches = find_regex_matches_backward(data, &re);
343
344 if matches.is_empty() {
345 out.write_all(data)?;
346 return Ok(());
347 }
348
349 if !before {
350 let last_end = matches.last().unwrap().1;
351 let has_trailing_sep = last_end == data.len();
352 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(matches.len() + 4);
353
354 if !has_trailing_sep {
356 slices.push(IoSlice::new(&data[last_end..]));
357 }
358
359 let mut i = matches.len();
360 while i > 0 {
361 i -= 1;
362 let rec_start = if i == 0 { 0 } else { matches[i - 1].1 };
363 let rec_end = matches[i].1;
364 slices.push(IoSlice::new(&data[rec_start..rec_end]));
365 }
366
367 write_all_slices(out, &slices)?;
368 } else {
369 let mut slices: Vec<IoSlice<'_>> = Vec::with_capacity(matches.len() + 2);
370
371 let mut i = matches.len();
372 while i > 0 {
373 i -= 1;
374 let start = matches[i].0;
375 let end = if i + 1 < matches.len() {
376 matches[i + 1].0
377 } else {
378 data.len()
379 };
380 slices.push(IoSlice::new(&data[start..end]));
381 }
382
383 if matches[0].0 > 0 {
384 slices.push(IoSlice::new(&data[..matches[0].0]));
385 }
386
387 write_all_slices(out, &slices)?;
388 }
389
390 Ok(())
391}