@@ -14,9 +14,15 @@ use std::{
1414 path:: { Path , PathBuf } ,
1515} ;
1616
17- use anyhow:: { Context , Result , bail, ensure} ;
17+ use anyhow:: { Context , Result , anyhow , bail, ensure} ;
1818use fn_error_context:: context;
19- use pcre2:: bytes:: Regex ;
19+ use pcre2:: bytes:: Regex as PcreRegex ;
20+ // the meta regex engine might be able to beat out specifying
21+ // another engine sometimes, since it kind of just redirects to a different engine,
22+ // depending on regex, so might as well use it.
23+ // It's also fairly easy to swap out here if desired, since really only
24+ // two methods need to be supported.
25+ use regex_automata:: meta:: Regex as AutomataRegex ;
2026use rustix:: {
2127 fd:: AsFd ,
2228 fs:: { Mode , OFlags , openat} ,
@@ -101,10 +107,47 @@ fn process_spec_file(
101107 Ok ( ( ) )
102108}
103109
110+ /* Most Rust crates don't support lookarounds and other features PCRE2 does, but they perform
111+ * a lot better when the regex being parsed doesn't involve them. So, we use the Rust
112+ * regex parsers, and if they error out, we'll fall back to PCRE2. */
113+
114+ enum RegexImpl {
115+ Automata ( AutomataRegex ) ,
116+ Pcre2 ( PcreRegex ) ,
117+ }
118+
119+ impl RegexImpl {
120+ #[ inline( always) ]
121+ fn new ( pattern : & str ) -> Result < Self > {
122+ AutomataRegex :: new ( pattern)
123+ . map ( RegexImpl :: Automata )
124+ . or_else ( |automata_err| {
125+ PcreRegex :: new ( pattern)
126+ . map ( RegexImpl :: Pcre2 )
127+ . map_err ( |pcre_err| {
128+ anyhow ! (
129+ "Failed to compile regex `{}` with regex_automata: {}; PCRE2 fallback failed: {}" ,
130+ pattern,
131+ automata_err,
132+ pcre_err
133+ )
134+ } )
135+ } )
136+ }
137+
138+ #[ inline( always) ]
139+ fn is_match ( & self , input : & [ u8 ] ) -> bool {
140+ match self {
141+ RegexImpl :: Automata ( re) => re. is_match ( input) ,
142+ RegexImpl :: Pcre2 ( re) => re. is_match ( input) . unwrap_or ( false ) ,
143+ }
144+ }
145+ }
146+
104147struct Policy {
105148 aliases : HashMap < OsString , OsString > ,
106- regexes : Vec < Regex > ,
107- contexts : Vec < String > ,
149+ // (regex, context)
150+ rules : Vec < ( RegexImpl , String ) > ,
108151}
109152
110153/// Open a file in the composefs store, handling inline vs external files.
@@ -172,16 +215,14 @@ impl Policy {
172215 regexps. reverse ( ) ;
173216 contexts. reverse ( ) ;
174217
175- let mut compiled = Vec :: with_capacity ( regexps. len ( ) ) ;
176- for r in & regexps {
177- compiled. push ( Regex :: new ( r) . with_context ( || format ! ( "Compiling PCRE2 regex: {r}" ) ) ?) ;
218+ let mut rules = Vec :: with_capacity ( regexps. len ( ) ) ;
219+ for ( re_src, context) in regexps. into_iter ( ) . zip ( contexts. into_iter ( ) ) {
220+ let regex =
221+ RegexImpl :: new ( & re_src) . with_context ( || format ! ( "Compiling regex: {re_src}" ) ) ?;
222+ rules. push ( ( regex, context) ) ;
178223 }
179224
180- Ok ( Policy {
181- aliases,
182- regexes : compiled,
183- contexts,
184- } )
225+ Ok ( Policy { aliases, rules } )
185226 }
186227
187228 pub fn check_aliased ( & self , filename : & OsStr ) -> Option < & OsStr > {
@@ -191,10 +232,10 @@ impl Policy {
191232 pub fn lookup ( & self , filename : & OsStr , ifmt : u8 ) -> Option < & str > {
192233 let key = & [ filename. as_bytes ( ) , & [ ifmt] ] . concat ( ) ;
193234
194- for ( i , re ) in self . regexes . iter ( ) . enumerate ( ) {
195- if re . is_match ( key) . unwrap_or ( false ) {
196- let ctx = self . contexts [ i ] . as_str ( ) ;
197- return ( ctx != "<<none>>" ) . then_some ( ctx ) ;
235+ for rule in & self . rules {
236+ if rule . 0 . is_match ( & key) {
237+ let context = rule . 1 . as_str ( ) ;
238+ return ( context != "<<none>>" ) . then_some ( context ) ;
198239 }
199240 }
200241 None
0 commit comments