tracexec_core/primitives/
regex.rs

1use std::sync::LazyLock;
2
3/// Regex and regex-cursor related code
4pub use regex_cursor::*;
5
6use crate::event::OutputMsg;
7
8pub trait BidirectionalIterator: Iterator {
9  fn prev(&mut self) -> Option<Self::Item>;
10}
11
12pub trait IntoBidirectionalIterator {
13  type Iter: BidirectionalIterator;
14
15  fn into_bidirectional_iter(self) -> Self::Iter;
16}
17
18pub struct BidirectionalIter<'a, T> {
19  slice: &'a [T],
20  index: BidirectionalIterIndex,
21}
22
23impl<'a, T> BidirectionalIter<'a, T> {
24  fn new(slice: &'a [T]) -> Self {
25    Self {
26      slice,
27      index: BidirectionalIterIndex::Start,
28    }
29  }
30}
31
32impl<T> BidirectionalIter<'_, T> {
33  pub fn by_ref(&mut self) -> &mut Self {
34    self
35  }
36}
37
38impl<'a, T> Iterator for BidirectionalIter<'a, T> {
39  type Item = &'a T;
40
41  fn next(&mut self) -> Option<Self::Item> {
42    self.index.next(self.slice.len());
43    self.index.get(self.slice)
44  }
45}
46
47impl<T> BidirectionalIterator for BidirectionalIter<'_, T> {
48  fn prev(&mut self) -> Option<Self::Item> {
49    self.index.prev(self.slice.len());
50    self.index.get(self.slice)
51  }
52}
53
54enum BidirectionalIterIndex {
55  Start,
56  Index(usize),
57  End,
58}
59
60impl BidirectionalIterIndex {
61  fn next(&mut self, len: usize) {
62    match self {
63      Self::Start => {
64        *self = if len > 0 { Self::Index(0) } else { Self::Start };
65      }
66      Self::Index(index) => {
67        if *index + 1 < len {
68          *index += 1;
69        } else {
70          *self = Self::End;
71        }
72      }
73      Self::End => {}
74    }
75  }
76
77  fn prev(&mut self, len: usize) {
78    match self {
79      Self::Start => {}
80      Self::Index(index) => {
81        if *index > 0 {
82          *index -= 1;
83        } else {
84          *self = Self::Start;
85        }
86      }
87      Self::End => {
88        *self = Self::Index(len.saturating_sub(1));
89      }
90    }
91  }
92
93  fn get<'a, T>(&self, slice: &'a [T]) -> Option<&'a T> {
94    match self {
95      Self::Start => None,
96      Self::Index(index) => slice.get(*index),
97      Self::End => None,
98    }
99  }
100}
101
102impl<'a, T> IntoBidirectionalIterator for &'a [T] {
103  type Iter = BidirectionalIter<'a, T>;
104
105  fn into_bidirectional_iter(self) -> Self::Iter {
106    BidirectionalIter::new(self)
107  }
108}
109
110enum BidirectionalInterspersedIterIndex {
111  Start,
112  Index(usize),
113  // The separator after the index
114  Separator(usize),
115  End,
116}
117
118pub struct BidirectionalInterspersedIter<'a, T> {
119  slice: &'a [T],
120  index: BidirectionalInterspersedIterIndex,
121  separator: &'a T,
122}
123
124impl<'a, T> BidirectionalInterspersedIter<'a, T> {
125  fn new(slice: &'a [T], separator: &'a T) -> Self {
126    Self {
127      slice,
128      index: BidirectionalInterspersedIterIndex::Start,
129      separator,
130    }
131  }
132}
133
134impl BidirectionalInterspersedIterIndex {
135  fn next(&mut self, len: usize) {
136    match self {
137      Self::Start => {
138        *self = if len > 0 { Self::Index(0) } else { Self::Start };
139      }
140      Self::Index(index) => {
141        if *index + 1 < len {
142          // Not last one
143          *self = Self::Separator(*index)
144        } else {
145          *self = Self::End;
146        }
147      }
148      Self::End => {}
149      Self::Separator(index) => *self = Self::Index(*index + 1),
150    }
151  }
152
153  fn prev(&mut self, len: usize) {
154    match self {
155      Self::Start => {}
156      Self::Index(index) => {
157        if *index > 0 {
158          // Not first one
159          *self = Self::Separator(*index - 1)
160        } else {
161          *self = Self::Start;
162        }
163      }
164      Self::End => {
165        if len > 0 {
166          *self = Self::Index(len.saturating_sub(1));
167        } else {
168          *self = Self::Start;
169        }
170      }
171      Self::Separator(index) => *self = Self::Index(*index),
172    }
173  }
174
175  fn get<'a, T>(&self, slice: &'a [T], separator: &'a T) -> Option<&'a T> {
176    match self {
177      Self::Start => None,
178      Self::Index(index) => slice.get(*index),
179      Self::Separator(_) => Some(separator),
180      Self::End => None,
181    }
182  }
183}
184
185impl<'a, T> Iterator for BidirectionalInterspersedIter<'a, T> {
186  type Item = &'a T;
187
188  fn next(&mut self) -> Option<Self::Item> {
189    self.index.next(self.slice.len());
190    self.index.get(self.slice, self.separator)
191  }
192}
193
194impl<T> BidirectionalIterator for BidirectionalInterspersedIter<'_, T> {
195  fn prev(&mut self) -> Option<Self::Item> {
196    self.index.prev(self.slice.len());
197    self.index.get(self.slice, self.separator)
198  }
199}
200
201#[cfg(test)]
202mod iter_tests {
203  use super::BidirectionalInterspersedIter;
204  use super::BidirectionalIterator;
205
206  #[test]
207  fn biter_interspersed_roundtrip() {
208    let slice = [1, 2, 3, 4];
209    let separator = 0;
210    let mut biter = BidirectionalInterspersedIter::new(&slice, &separator);
211    assert_eq!(biter.next(), Some(&1));
212    assert_eq!(biter.next(), Some(&0));
213    assert_eq!(biter.next(), Some(&2));
214    assert_eq!(biter.next(), Some(&0));
215    assert_eq!(biter.next(), Some(&3));
216    assert_eq!(biter.next(), Some(&0));
217    assert_eq!(biter.next(), Some(&4));
218    assert_eq!(biter.next(), None);
219    assert_eq!(biter.prev(), Some(&4));
220    assert_eq!(biter.prev(), Some(&0));
221    assert_eq!(biter.prev(), Some(&3));
222    assert_eq!(biter.prev(), Some(&0));
223    assert_eq!(biter.prev(), Some(&2));
224    assert_eq!(biter.prev(), Some(&0));
225    assert_eq!(biter.prev(), Some(&1));
226    assert_eq!(biter.prev(), None);
227  }
228
229  #[test]
230  fn biter_interspersed_two_items() {
231    let slice = [1, 2];
232    let separator = 0;
233    let mut biter = BidirectionalInterspersedIter::new(&slice, &separator);
234    assert_eq!(biter.next(), Some(&1));
235    assert_eq!(biter.next(), Some(&0));
236    assert_eq!(biter.next(), Some(&2));
237    assert_eq!(biter.prev(), Some(&0));
238    assert_eq!(biter.next(), Some(&2));
239    assert_eq!(biter.next(), None);
240    assert_eq!(biter.next(), None);
241    assert_eq!(biter.prev(), Some(&2));
242    assert_eq!(biter.prev(), Some(&0));
243    assert_eq!(biter.prev(), Some(&1));
244    assert_eq!(biter.prev(), None);
245    assert_eq!(biter.prev(), None);
246    assert_eq!(biter.next(), Some(&1));
247    assert_eq!(biter.next(), Some(&0));
248    assert_eq!(biter.prev(), Some(&1));
249  }
250
251  #[test]
252  fn biter_interspersed_single_item() {
253    let slice = [1];
254    let separator = 0;
255    let mut biter = BidirectionalInterspersedIter::new(&slice, &separator);
256    assert_eq!(biter.next(), Some(&1));
257    assert_eq!(biter.next(), None);
258    assert_eq!(biter.next(), None);
259    assert_eq!(biter.prev(), Some(&1));
260    assert_eq!(biter.prev(), None);
261  }
262}
263
264// Original Copyright Notice for the following code:
265
266// Copyright (c) 2024 Pascal Kuthe
267
268// Permission is hereby granted, free of charge, to any
269// person obtaining a copy of this software and associated
270// documentation files (the "Software"), to deal in the
271// Software without restriction, including without
272// limitation the rights to use, copy, modify, merge,
273// publish, distribute, sublicense, and/or sell copies of
274// the Software, and to permit persons to whom the Software
275// is furnished to do so, subject to the following
276// conditions:
277
278// The above copyright notice and this permission notice
279// shall be included in all copies or substantial portions
280// of the Software.
281
282// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
283// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
284// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
285// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
286// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
287// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
288// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
289// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
290// DEALINGS IN THE SOFTWARE.
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293enum CursorPosition {
294  ChunkStart,
295  ChunkEnd,
296}
297
298/// Argv joined by a single white space between arguments
299pub struct ArgvCursor<'a, T> {
300  iter: BidirectionalInterspersedIter<'a, T>,
301  current: &'a [u8],
302  position: CursorPosition,
303  len: usize,
304  offset: usize,
305}
306
307pub static SPACE: LazyLock<OutputMsg> = LazyLock::new(|| OutputMsg::Ok(" ".into()));
308
309impl<'a, T> ArgvCursor<'a, T>
310where
311  T: AsRef<str>,
312{
313  pub fn new(slice: &'a [T], separator: &'a T) -> Self {
314    let mut res = Self {
315      iter: BidirectionalInterspersedIter::new(slice, separator),
316      current: &[],
317      position: CursorPosition::ChunkEnd,
318      len: slice.iter().map(|s| s.as_ref().len()).sum::<usize>()
319        + (slice.len().saturating_sub(1)) * separator.as_ref().len(),
320      offset: 0,
321    };
322    res.advance();
323    res
324  }
325}
326
327impl<T> Cursor for ArgvCursor<'_, T>
328where
329  T: AsRef<str>,
330{
331  fn chunk(&self) -> &[u8] {
332    self.current
333  }
334
335  fn advance(&mut self) -> bool {
336    match self.position {
337      CursorPosition::ChunkStart => {
338        self.iter.next();
339        self.position = CursorPosition::ChunkEnd;
340      }
341      CursorPosition::ChunkEnd => (),
342    }
343    for next in self.iter.by_ref() {
344      if next.as_ref().is_empty() {
345        continue;
346      }
347      self.offset += self.current.len();
348      self.current = next.as_ref().as_bytes();
349      return true;
350    }
351    false
352  }
353
354  fn backtrack(&mut self) -> bool {
355    match self.position {
356      CursorPosition::ChunkStart => {}
357      CursorPosition::ChunkEnd => {
358        self.iter.prev();
359        self.position = CursorPosition::ChunkStart;
360      }
361    }
362    while let Some(prev) = self.iter.prev() {
363      if prev.as_ref().is_empty() {
364        continue;
365      }
366      self.offset -= prev.as_ref().len();
367      self.current = prev.as_ref().as_bytes();
368      return true;
369    }
370    false
371  }
372
373  fn total_bytes(&self) -> Option<usize> {
374    Some(self.len)
375  }
376
377  fn offset(&self) -> usize {
378    self.offset
379  }
380}
381
382#[cfg(test)]
383mod cursor_tests {
384  use super::*;
385  use crate::cache::ArcStr;
386
387  #[test]
388  fn smoke_test() {
389    let single = vec![ArcStr::from("abc")];
390    let separator = " ".into();
391    let mut cursor = ArgvCursor::new(single.as_slice(), &separator);
392    assert_eq!(cursor.chunk(), b"abc");
393    assert!(!cursor.advance());
394    assert_eq!(cursor.chunk(), b"abc");
395    assert!(!cursor.backtrack());
396    assert_eq!(cursor.chunk(), b"abc");
397    // 2
398    let two = vec![ArcStr::from("abc"); 2];
399    let mut cursor = ArgvCursor::new(two.as_slice(), &separator);
400    let mut offset = 0;
401    loop {
402      assert_eq!(cursor.offset(), offset);
403      offset += cursor.chunk().len();
404      if !cursor.advance() {
405        break;
406      }
407    }
408    loop {
409      offset -= cursor.chunk().len();
410      assert_eq!(cursor.offset(), offset);
411      if !cursor.backtrack() {
412        break;
413      }
414    }
415    assert_eq!(cursor.offset(), 0);
416    assert_eq!(offset, 0);
417    // multi
418    let multi = vec![ArcStr::from("abc"); 100];
419    let mut cursor = ArgvCursor::new(multi.as_slice(), &separator);
420    let mut offset = 0;
421    loop {
422      assert_eq!(cursor.offset(), offset);
423      offset += cursor.chunk().len();
424      if !cursor.advance() {
425        break;
426      }
427    }
428    loop {
429      offset -= cursor.chunk().len();
430      assert_eq!(cursor.offset(), offset);
431      if !cursor.backtrack() {
432        break;
433      }
434    }
435    assert_eq!(cursor.offset(), 0);
436    assert_eq!(offset, 0);
437  }
438}