bmatcher_core/pattern.rs
1use alloc::borrow::Cow;
2use core::fmt::Debug;
3
4use crate::Atom;
5
6/// A binary pattern is a structure used in matching processes and consists of two main components:
7///
8/// 1. Atoms
9/// A list of instructions or actions that define how the matcher should process the data at the current cursor.
10///
11/// 2. Byte Sequence
12/// A sequence of bytes that the atoms can reference to match against actual input data.
13pub trait BinaryPattern: Send + Sync + Debug {
14 /// Retrieves the list of atoms within this binary pattern.
15 ///
16 /// The atoms define the actions or matching logic for the pattern.
17 fn atoms(&self) -> &[Atom];
18
19 /// Retrieves the byte sequence referenced by the atoms.
20 ///
21 /// This sequence represents the actual data to be matched against.
22 fn byte_sequence(&self) -> &[u8];
23
24 /// Returns an upper bound for the length of the save stack.
25 ///
26 /// # Note
27 /// This is only an upper bound. The actual length used might be smaller.
28 fn save_len(&self) -> usize {
29 self.atoms()
30 .iter()
31 .filter(|atom| {
32 matches!(
33 atom,
34 Atom::Read(_) | Atom::SaveCursor | Atom::SaveConstant(_)
35 )
36 })
37 .count()
38 }
39
40 /// Returns an upper bound for the length of the cursor stack.
41 ///
42 /// # Note
43 /// This is only an upper bound. The actual length used might be smaller.
44 fn cursor_len(&self) -> usize {
45 self.atoms()
46 .iter()
47 .filter(|atom| matches!(atom, Atom::CursorPush))
48 .count()
49 }
50}
51
52/// A flexible implementation of the [`BinaryPattern`] interface supporting both borrowed and owned data.
53///
54/// This struct uses [`alloc::borrow::Cow`] for both the [`Atom`] array and the byte sequence,
55/// allowing it to either borrow data without allocation or own it when necessary.
56/// This design makes it suitable for both compile-time defined and runtime-parsed patterns.
57///
58/// # Example
59/// ```
60/// # use bmatcher_core::*;
61/// let atoms = &[Atom::ByteSequence { seq_start: 0x00, seq_end: 0x01 }];
62/// let bytes = &[0x90];
63///
64/// // Borrowed (no allocation)
65/// let borrowed = GenericBinaryPattern::new(atoms, bytes);
66///
67/// // Owned (allocating)
68/// let owned = GenericBinaryPattern::new(vec![Atom::ByteSequence { seq_start: 0x00, seq_end: 0x01 }], vec![0x90]);
69#[derive(Debug, Clone)]
70pub struct GenericBinaryPattern<'a> {
71 atoms: Cow<'a, [Atom]>,
72 byte_sequence: Cow<'a, [u8]>,
73}
74
75impl<'a> GenericBinaryPattern<'a> {
76 /// Creates a new [`GenericBinaryPattern`] from borrowed or owned data.
77 ///
78 /// This constructor accepts any type that can be converted into a [`Cow`],
79 /// such as slices or vectors.
80 /// Borrowed inputs (`&[Atom]`, `&[u8]`) avoid allocation, while owned inputs
81 /// (`Vec<Atom>`, `Vec<u8>`) store the data internally.
82 pub fn new(atoms: impl Into<Cow<'a, [Atom]>>, byte_sequence: impl Into<Cow<'a, [u8]>>) -> Self {
83 Self {
84 atoms: atoms.into(),
85 byte_sequence: byte_sequence.into(),
86 }
87 }
88
89 /// Creates a borrowed [`GenericBinaryPattern`] from static references.
90 ///
91 /// This constructor is `const` and does not perform any heap allocation.
92 /// It is ideal for defining patterns that reference constant or static data.
93 pub const fn new_const(atoms: &'a [Atom], byte_sequence: &'a [u8]) -> Self {
94 Self {
95 atoms: Cow::Borrowed(atoms),
96 byte_sequence: Cow::Borrowed(byte_sequence),
97 }
98 }
99
100 /// Converts the pattern into an owned version with a `'static` lifetime.
101 ///
102 /// Any borrowed data is cloned into owned memory, ensuring that the returned
103 /// pattern no longer depends on the original lifetimes.
104 /// This is useful when the pattern needs to outlive temporary references.
105 pub fn into_owned(self) -> GenericBinaryPattern<'static> {
106 GenericBinaryPattern {
107 atoms: self.atoms.into_owned().into(),
108 byte_sequence: self.byte_sequence.into_owned().into(),
109 }
110 }
111}
112
113impl BinaryPattern for GenericBinaryPattern<'_> {
114 fn atoms(&self) -> &[Atom] {
115 self.atoms.as_ref()
116 }
117
118 fn byte_sequence(&self) -> &[u8] {
119 self.byte_sequence.as_ref()
120 }
121}