1use std::collections::HashMap;
17use std::marker::PhantomData;
18use std::ops::Index;
19use std::os::raw::c_char;
20use std::sync::Arc;
21
22pub const VERSION: &str = env!("CARGO_PKG_VERSION");
24
25enum RealRegex {}
27enum RealIter {}
28enum RealRegexSet {}
29
30extern "C" {
31 fn real_compile(pattern: *const c_char, len: usize, flags: u32,
32 errbuf: *mut c_char, errbuf_len: usize, code: *mut i32) -> *mut RealRegex;
33 fn real_group_count(re: *const RealRegex) -> usize;
34 fn real_group_name(re: *const RealRegex, group: usize, buf: *mut c_char, buflen: usize) -> usize;
35 fn real_free(re: *mut RealRegex);
36 fn real_find_iter(re: *const RealRegex, text: *const c_char, len: usize) -> *mut RealIter;
37 fn real_find_iter_at(re: *const RealRegex, text: *const c_char, len: usize, start: usize) -> *mut RealIter;
38 fn real_iter_next(iter: *mut RealIter, spans: *mut usize) -> i32;
39 fn real_iter_free(iter: *mut RealIter);
40 fn real_count_matches(re: *const RealRegex, text: *const c_char, len: usize) -> usize;
41 fn real_set_compile(patterns: *const *const c_char, lens: *const usize, n: usize, flags: u32,
42 errbuf: *mut c_char, errbuf_len: usize, code: *mut i32) -> *mut RealRegexSet;
43 fn real_set_size(set: *const RealRegexSet) -> usize;
44 fn real_set_free(set: *mut RealRegexSet);
45 fn real_set_is_match(set: *const RealRegexSet, text: *const c_char, len: usize) -> i32;
46 fn real_set_matches(set: *const RealRegexSet, text: *const c_char, len: usize, out: *mut u8) -> i32;
47}
48
49const DIVERGENCES_URL: &str = "https://github.com/RECHE23/real-regex/blob/main/docs/COMPATIBILITY.md";
50const REAL_ERR_UNSUPPORTED: i32 = 2; const DOLLAR_ENDONLY: u32 = 128;
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum Error {
58 Syntax { msg: String, pos: Option<usize> },
60 Unsupported { construct: String, hint: String },
66}
67
68impl Error {
69 pub fn is_unsupported(&self) -> bool {
71 matches!(self, Error::Unsupported { .. })
72 }
73
74 fn from_engine(raw: &str, code: i32, rescue: Rescue) -> Error {
78 let body = raw.strip_prefix("regex_error").unwrap_or(raw).trim_start();
79 let (pos, msg) = match body.strip_prefix("at ").and_then(|r| r.split_once(':')) {
80 Some((n, rest)) => (n.trim().parse::<usize>().ok(), rest.trim().to_string()),
81 None => (None, body.trim_start_matches(':').trim().to_string()),
82 };
83 if code == REAL_ERR_UNSUPPORTED {
84 unsupported_construct(&msg, rescue)
85 } else {
86 Error::Syntax { msg, pos }
87 }
88 }
89}
90
91impl std::fmt::Display for Error {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 match self {
94 Error::Syntax { msg, pos: Some(p) } => write!(f, "syntax error at {p}: {msg}"),
95 Error::Syntax { msg, pos: None } => write!(f, "syntax error: {msg}"),
96 Error::Unsupported { construct, hint } => write!(f, "{construct} ({hint})"),
97 }
98 }
99}
100
101impl std::error::Error for Error {}
102
103struct GroupInfo {
106 names: Vec<Option<String>>, by_name: HashMap<String, usize>, }
109
110const CAPS_INLINE_SLOTS: usize = 8;
113
114#[derive(Clone, Debug)]
125enum SlotStore {
126 Inline { len: u8, slots: [usize; CAPS_INLINE_SLOTS] },
127 Spilled(Box<[usize]>),
128}
129
130impl SlotStore {
131 fn from_flat(src: &[usize]) -> SlotStore {
133 if src.len() == 2 {
141 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
142 slots[0] = src[0];
143 slots[1] = src[1];
144 return SlotStore::Inline { len: 2, slots };
145 }
146 if src.len() <= CAPS_INLINE_SLOTS {
147 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
148 slots[..src.len()].copy_from_slice(src);
149 SlotStore::Inline { len: src.len() as u8, slots }
150 } else {
151 SlotStore::Spilled(src.to_vec().into_boxed_slice())
152 }
153 }
154
155 fn as_slice(&self) -> &[usize] {
156 match self {
157 SlotStore::Inline { len, slots } => &slots[..*len as usize],
158 SlotStore::Spilled(b) => b,
159 }
160 }
161
162 fn group(&self, i: usize) -> Option<(usize, usize)> {
169 let s = self.as_slice();
170 let lo = i.checked_mul(2)?;
171 let a = *s.get(lo)?;
172 let b = *s.get(lo + 1)?;
173 if a == usize::MAX {
174 None
175 } else {
176 Some((a, b))
177 }
178 }
179
180 fn ngroups(&self) -> usize {
182 self.as_slice().len() / 2
183 }
184}
185
186#[cfg_attr(not(feature = "fallback"), allow(dead_code))]
195enum Rescue {
196 Delegable,
198 Refused,
200 Unknown,
202 NoFallbackHere,
204}
205
206#[cfg(feature = "fallback")]
210fn rescue_for(pattern: &[u8]) -> Rescue {
211 match std::str::from_utf8(pattern) {
212 Ok(p) if regex::Regex::new(p).is_ok() => Rescue::Delegable,
213 Ok(_) => Rescue::Refused,
214 Err(_) => Rescue::Unknown,
215 }
216}
217
218#[cfg(not(feature = "fallback"))]
219fn rescue_for(_pattern: &[u8]) -> Rescue {
220 Rescue::Unknown
221}
222
223fn unsupported_construct(construct: &str, rescue: Rescue) -> Error {
225 let remedy = match rescue {
226 Rescue::Delegable => "the `fallback` feature plus `RegexBuilder::fallback(true)` delegates this \
227 pattern to the regex crate (forfeiting the linear-time guarantee for it)"
228 .to_string(),
229 Rescue::Refused => "the `fallback` feature does not help here: the regex crate is linear too and \
230 refuses this pattern as well"
231 .to_string(),
232 Rescue::Unknown => "the `fallback` feature delegates some such patterns to the regex crate, but not \
233 a non-regular one (a backreference, a conditional) — the regex crate, linear \
234 itself, refuses those too"
235 .to_string(),
236 Rescue::NoFallbackHere => "a RegexSet never delegates: compile the pattern on its own with the \
237 `fallback` feature if you need it"
238 .to_string(),
239 };
240 Error::Unsupported {
241 construct: construct.to_string(),
242 hint: format!("unsupported by REAL — see {DIVERGENCES_URL} ; {remedy}"),
243 }
244}
245
246fn nested_class_syntax(pattern: &[u8]) -> Option<&'static str> {
253 let mut i = 0;
254 let mut in_class = false;
255 let mut class_pos = 0usize; while i < pattern.len() {
257 let b = pattern[i];
258 if b == b'\\' {
259 i += 2; if in_class {
261 class_pos += 1;
262 }
263 continue;
264 }
265 if !in_class {
266 if b == b'[' {
267 in_class = true;
268 class_pos = 0;
269 if pattern.get(i + 1) == Some(&b'^') {
270 i += 1; }
272 }
273 } else if b == b']' {
274 if class_pos == 0 {
275 class_pos += 1; } else {
277 in_class = false;
278 }
279 } else if b == b'[' {
280 return Some("nested character class");
281 } else if matches!(b, b'&' | b'-' | b'~') && pattern.get(i + 1) == Some(&b) {
282 return Some("character-class set operation");
283 } else {
284 class_pos += 1;
285 }
286 i += 1;
287 }
288 None
289}
290
291fn compile_handle(pattern: &[u8], flags: u32) -> Result<(*mut RealRegex, usize, Arc<GroupInfo>), Error> {
293 if let Some(construct) = nested_class_syntax(pattern) {
294 return Err(unsupported_construct(construct, rescue_for(pattern))); }
296 let mut err = [0u8; 256];
297 let mut code: i32 = 0;
298 let handle = unsafe {
299 real_compile(pattern.as_ptr() as *const c_char, pattern.len(), flags | DOLLAR_ENDONLY,
300 err.as_mut_ptr() as *mut c_char, err.len(), &mut code)
301 };
302 if handle.is_null() {
303 let end = err.iter().position(|&b| b == 0).unwrap_or(err.len());
304 return Err(Error::from_engine(&String::from_utf8_lossy(&err[..end]), code, rescue_for(pattern)));
305 }
306 let ngroups = unsafe { real_group_count(handle) };
307 let mut names = Vec::with_capacity(ngroups);
308 let mut by_name = HashMap::new();
309 for g in 0..ngroups {
312 let len = unsafe { real_group_name(handle, g, std::ptr::null_mut(), 0) };
313 if len == 0 {
314 names.push(None);
315 } else {
316 let mut buf = vec![0u8; len + 1];
317 unsafe {
318 real_group_name(handle, g, buf.as_mut_ptr() as *mut c_char, buf.len());
319 }
320 let name = String::from_utf8_lossy(&buf[..len]).into_owned();
321 by_name.insert(name.clone(), g);
322 names.push(Some(name));
323 }
324 }
325 Ok((handle, ngroups, Arc::new(GroupInfo { names, by_name })))
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub enum Engine {
331 Real,
333 Fallback,
335}
336
337pub struct Regex {
339 handle: *mut RealRegex, ngroups: usize, pattern: String,
342 groups: Arc<GroupInfo>,
343 #[cfg(feature = "fallback")]
344 fallback: Option<regex::Regex>, }
346
347unsafe impl Send for Regex {}
350unsafe impl Sync for Regex {}
351
352impl Regex {
353 pub fn new(pattern: &str) -> Result<Regex, Error> {
356 Regex::with_flags(pattern, 0)
357 }
358
359 pub fn with_flags(pattern: &str, flags: u32) -> Result<Regex, Error> {
362 let (handle, ngroups, groups) = compile_handle(pattern.as_bytes(), flags)?;
363 Ok(Regex {
364 handle,
365 ngroups,
366 pattern: pattern.to_string(),
367 groups,
368 #[cfg(feature = "fallback")]
369 fallback: None,
370 })
371 }
372
373 pub fn engine(&self) -> Engine {
376 #[cfg(feature = "fallback")]
377 if self.fallback.is_some() {
378 return Engine::Fallback;
379 }
380 Engine::Real
381 }
382
383 #[cfg(feature = "fallback")]
386 fn build_fallback(pattern: &str, flags: u32) -> Result<Regex, Error> {
387 let fb = regex::RegexBuilder::new(pattern)
388 .case_insensitive(flags & FLAG_ICASE != 0)
389 .multi_line(flags & FLAG_MULTILINE != 0)
390 .dot_matches_new_line(flags & FLAG_DOTALL != 0)
391 .ignore_whitespace(flags & FLAG_VERBOSE != 0)
392 .unicode(flags & FLAG_ASCII == 0)
393 .build()
394 .map_err(|e| Error::Syntax { msg: e.to_string(), pos: None })?;
395 let ngroups = fb.captures_len();
396 let mut names = Vec::with_capacity(ngroups);
397 let mut by_name = HashMap::new();
398 for (i, n) in fb.capture_names().enumerate() {
399 match n {
400 Some(name) => {
401 by_name.insert(name.to_string(), i);
402 names.push(Some(name.to_string()));
403 }
404 None => names.push(None),
405 }
406 }
407 Ok(Regex {
408 handle: std::ptr::null_mut(),
409 ngroups,
410 pattern: pattern.to_string(),
411 groups: Arc::new(GroupInfo { names, by_name }),
412 fallback: Some(fb),
413 })
414 }
415
416 pub fn as_str(&self) -> &str {
418 &self.pattern
419 }
420
421 pub fn captures_len(&self) -> usize {
424 self.ngroups
425 }
426
427 pub fn capture_names(&self) -> impl Iterator<Item = Option<&str>> {
429 self.groups.names.iter().map(|o| o.as_deref())
430 }
431
432 fn raw<'r, 't>(&'r self, text: &'t str, start: Option<usize>) -> SpanCursor<'r, 't> {
433 #[cfg(feature = "fallback")]
434 if let Some(fb) = &self.fallback {
435 return SpanCursor::Fallback {
436 it: fb.captures_iter(text),
437 ngroups: self.ngroups,
438 min_start: start.unwrap_or(0),
439 cur: Vec::new(),
440 };
441 }
442 let iter = unsafe {
443 match start {
444 None => real_find_iter(self.handle, text.as_ptr() as *const c_char, text.len()),
445 Some(s) => real_find_iter_at(self.handle, text.as_ptr() as *const c_char, text.len(), s),
446 }
447 };
448 assert!(!iter.is_null(), "real-regex: engine iteration failed");
450 SpanCursor::Real(RawSpans { iter, handle: self.handle, text: text.as_bytes(), ngroups: self.ngroups, buf: vec![0usize; 2 * self.ngroups], last_end: None, drive_pos: None, utf8: true, _re: PhantomData })
451 }
452
453 fn caps_from<'t>(&self, text: &'t str, cur: &SpanCursor<'_, '_>) -> Captures<'t> {
454 Captures { text, slots: cur.slot_store(), groups: Arc::clone(&self.groups) }
455 }
456
457 pub fn is_match(&self, text: &str) -> bool {
459 self.raw(text, None).advance().is_some()
460 }
461
462 pub fn is_match_at(&self, text: &str, start: usize) -> bool {
464 self.raw(text, Some(start)).advance().is_some()
465 }
466
467 pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>> {
469 self.raw(text, None).advance().map(|(a, b)| Match { text, start: a, end: b })
470 }
471
472 pub fn find_at<'t>(&self, text: &'t str, start: usize) -> Option<Match<'t>> {
474 self.raw(text, Some(start)).advance().map(|(a, b)| Match { text, start: a, end: b })
475 }
476
477 pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> {
479 Matches { raw: self.raw(text, None), text }
480 }
481
482 pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
484 {
485 let mut c = self.raw(text, None);
486 c.advance().map(|_| self.caps_from(text, &c))
487 }
488 }
489
490 pub fn captures_at<'t>(&self, text: &'t str, start: usize) -> Option<Captures<'t>> {
492 {
493 let mut c = self.raw(text, Some(start));
494 c.advance().map(|_| self.caps_from(text, &c))
495 }
496 }
497
498 pub fn capture_locations(&self) -> CaptureLocations {
502 CaptureLocations {
503 slots: vec![0; 2 * self.ngroups],
504 ngroups: self.ngroups,
505 }
506 }
507
508 pub fn captures_read<'t>(
511 &self,
512 locs: &mut CaptureLocations,
513 text: &'t str,
514 ) -> Option<Match<'t>> {
515 self.captures_read_at(locs, text, 0)
516 }
517
518 pub fn captures_read_at<'t>(
520 &self,
521 locs: &mut CaptureLocations,
522 text: &'t str,
523 start: usize,
524 ) -> Option<Match<'t>> {
525 locs.ensure(self.ngroups);
526 let mut c = self.raw(text, if start == 0 { None } else { Some(start) });
527 let (a, b) = c.advance()?;
528 c.copy_slots_into(locs);
529 Some(Match {
530 text,
531 start: a,
532 end: b,
533 })
534 }
535
536 pub fn captures_read_iter<'r, 't>(
542 &'r self,
543 text: &'t str,
544 ) -> CaptureLocationMatches<'r, 't> {
545 CaptureLocationMatches {
546 raw: self.raw(text, None),
547 text,
548 ngroups: self.ngroups,
549 }
550 }
551
552 pub fn captures_iter<'r, 't>(&'r self, text: &'t str) -> CaptureMatches<'r, 't> {
554 CaptureMatches { raw: self.raw(text, None), re: self, text }
555 }
556
557 pub fn shortest_match(&self, text: &str) -> Option<usize> {
563 #[cfg(feature = "fallback")]
564 if let Some(fb) = &self.fallback {
565 return fb.shortest_match(text); }
567 self.raw(text, None).advance().map(|(_, e)| e)
568 }
569
570 pub fn count_matches(&self, text: &str) -> usize {
576 #[cfg(feature = "fallback")]
577 if let Some(fb) = &self.fallback {
578 return fb.find_iter(text).count();
579 }
580 let n = unsafe {
581 real_count_matches(self.handle, text.as_ptr() as *const c_char, text.len())
582 };
583 assert_ne!(n, usize::MAX, "real-regex: count_matches failed");
584 n
585 }
586}
587
588pub struct RegexSet {
595 handle: *mut RealRegexSet,
596 patterns: Vec<String>,
597}
598
599unsafe impl Send for RegexSet {}
600unsafe impl Sync for RegexSet {}
601
602impl RegexSet {
603 pub fn new<I, S>(patterns: I) -> Result<RegexSet, Error>
605 where
606 I: IntoIterator<Item = S>,
607 S: AsRef<str>,
608 {
609 RegexSet::with_flags(patterns, 0)
610 }
611
612 pub fn with_flags<I, S>(patterns: I, flags: u32) -> Result<RegexSet, Error>
614 where
615 I: IntoIterator<Item = S>,
616 S: AsRef<str>,
617 {
618 let owned: Vec<String> = patterns.into_iter().map(|s| s.as_ref().to_string()).collect();
619 let mut ptrs: Vec<*const c_char> = Vec::with_capacity(owned.len());
620 let mut lens: Vec<usize> = Vec::with_capacity(owned.len());
621 for p in &owned {
622 ptrs.push(p.as_ptr() as *const c_char);
623 lens.push(p.len());
624 }
625 let mut err = [0i8; 512];
626 let mut code: i32 = 0;
627 let handle = unsafe {
628 real_set_compile(
629 ptrs.as_ptr(),
630 lens.as_ptr(),
631 owned.len(),
632 flags | DOLLAR_ENDONLY,
633 err.as_mut_ptr(),
634 err.len(),
635 &mut code,
636 )
637 };
638 if handle.is_null() {
639 let raw = unsafe { std::ffi::CStr::from_ptr(err.as_ptr()) }
640 .to_string_lossy()
641 .into_owned();
642 return Err(Error::from_engine(&raw, code, Rescue::NoFallbackHere));
643 }
644 Ok(RegexSet {
645 handle,
646 patterns: owned,
647 })
648 }
649
650 pub fn len(&self) -> usize {
652 unsafe { real_set_size(self.handle) }
653 }
654
655 pub fn is_empty(&self) -> bool {
657 self.len() == 0
658 }
659
660 pub fn patterns(&self) -> &[String] {
662 &self.patterns
663 }
664
665 pub fn is_match(&self, text: &str) -> bool {
667 let r = unsafe {
668 real_set_is_match(self.handle, text.as_ptr() as *const c_char, text.len())
669 };
670 r == 1
671 }
672
673 pub fn matches(&self, text: &str) -> Vec<bool> {
676 let n = self.len();
677 let mut out = vec![0u8; n];
678 let r = unsafe {
679 real_set_matches(
680 self.handle,
681 text.as_ptr() as *const c_char,
682 text.len(),
683 out.as_mut_ptr(),
684 )
685 };
686 assert_eq!(r, 0, "real-regex: regex_set matches failed");
687 out.into_iter().map(|b| b != 0).collect()
688 }
689
690 pub fn matched_ids(&self, text: &str) -> Vec<usize> {
692 self.matches(text)
693 .into_iter()
694 .enumerate()
695 .filter_map(|(i, hit)| hit.then_some(i))
696 .collect()
697 }
698}
699
700impl Drop for RegexSet {
701 fn drop(&mut self) {
702 if !self.handle.is_null() {
703 unsafe { real_set_free(self.handle) }
704 }
705 }
706}
707
708impl Drop for Regex {
709 fn drop(&mut self) {
710 if !self.handle.is_null() {
711 unsafe { real_free(self.handle) } }
713 }
714}
715
716impl std::fmt::Debug for Regex {
717 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718 write!(f, "Regex({:?})", self.pattern)
719 }
720}
721
722impl std::fmt::Display for Regex {
723 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
725 f.write_str(self.as_str())
726 }
727}
728
729impl std::str::FromStr for Regex {
730 type Err = Error;
731
732 fn from_str(s: &str) -> Result<Regex, Error> {
733 Regex::new(s)
734 }
735}
736
737struct RawSpans<'r, 't> {
739 iter: *mut RealIter, handle: *const RealRegex, text: &'t [u8], ngroups: usize,
743 buf: Vec<usize>, last_end: Option<usize>, drive_pos: Option<usize>, utf8: bool, _re: PhantomData<&'r ()>, }
749
750impl RawSpans<'_, '_> {
751 fn advance(&mut self) -> Option<(usize, usize)> {
763 if self.drive_pos.is_some() {
764 return self.drive_advance();
765 }
766 loop {
767 let got = unsafe { real_iter_next(self.iter, self.buf.as_mut_ptr()) };
768 match got {
769 0 => return None,
770 -1 => panic!("real-regex: engine iteration failed"),
773 _ => {
774 let (s0, e0) = (self.buf[0], self.buf[1]); if s0 == e0 {
776 self.drive_pos = Some(self.last_end.unwrap_or(0));
779 return self.drive_advance();
780 }
781 self.last_end = Some(e0);
782 return Some((s0, e0));
783 }
784 }
785 }
786 }
787
788 fn search_at(&mut self, pos: usize) -> Option<(usize, usize)> {
791 if pos > self.text.len() {
792 return None;
793 }
794 let it = unsafe {
795 real_find_iter_at(self.handle, self.text.as_ptr() as *const c_char, self.text.len(), pos)
796 };
797 assert!(!it.is_null(), "real-regex: engine iteration failed");
798 let got = unsafe { real_iter_next(it, self.buf.as_mut_ptr()) };
799 unsafe { real_iter_free(it) };
800 match got {
801 0 => None,
802 -1 => panic!("real-regex: engine iteration failed"),
803 _ => Some((self.buf[0], self.buf[1])),
804 }
805 }
806
807 fn step_len(&self, pos: usize) -> usize {
810 if !self.utf8 || pos >= self.text.len() {
811 return 1;
812 }
813 match self.text[pos] {
814 b if b < 0x80 => 1,
815 b if b < 0xE0 => 2,
816 b if b < 0xF0 => 3,
817 _ => 4,
818 }
819 }
820
821 fn drive_advance(&mut self) -> Option<(usize, usize)> {
825 let pos = self.drive_pos.expect("drive_advance in fast mode");
826 let mut m = self.search_at(pos)?;
827 if m.0 == m.1 && Some(m.1) == self.last_end {
828 let next = m.1 + self.step_len(m.1);
829 m = self.search_at(next)?;
830 }
831 self.last_end = Some(m.1);
832 self.drive_pos = Some(m.1);
833 Some(m)
834 }
835
836}
837
838impl Drop for RawSpans<'_, '_> {
839 fn drop(&mut self) {
840 unsafe { real_iter_free(self.iter) }
841 }
842}
843
844enum SpanCursor<'r, 't> {
847 Real(RawSpans<'r, 't>),
848 #[cfg(feature = "fallback")]
849 Fallback {
850 it: regex::CaptureMatches<'r, 't>,
851 ngroups: usize,
852 min_start: usize,
853 cur: Vec<Option<(usize, usize)>>, },
855}
856
857impl SpanCursor<'_, '_> {
858 fn advance(&mut self) -> Option<(usize, usize)> {
862 match self {
863 SpanCursor::Real(r) => r.advance(),
864 #[cfg(feature = "fallback")]
865 SpanCursor::Fallback { it, ngroups, min_start, cur } => loop {
866 let caps = it.next()?;
867 let m0 = caps.get(0).unwrap();
868 if m0.start() < *min_start {
869 continue; }
871 cur.clear();
872 cur.extend((0..*ngroups).map(|g| caps.get(g).map(|m| (m.start(), m.end()))));
873 return Some((m0.start(), m0.end()));
874 },
875 }
876 }
877
878 fn nslots(&self) -> usize {
880 match self {
881 SpanCursor::Real(r) => 2 * r.ngroups,
882 #[cfg(feature = "fallback")]
883 SpanCursor::Fallback { ngroups, .. } => 2 * *ngroups,
884 }
885 }
886
887 fn write_slots(&self, out: &mut [usize]) {
891 match self {
892 SpanCursor::Real(r) => out.copy_from_slice(&r.buf),
893 #[cfg(feature = "fallback")]
894 SpanCursor::Fallback { cur, .. } => {
895 for (g, s) in cur.iter().enumerate() {
896 let (a, b) = s.unwrap_or((usize::MAX, usize::MAX));
897 out[2 * g] = a;
898 out[(2 * g) + 1] = b;
899 }
900 }
901 }
902 }
903
904 fn slot_store(&self) -> SlotStore {
906 match self {
907 SpanCursor::Real(r) => SlotStore::from_flat(&r.buf),
909 #[cfg(feature = "fallback")]
910 SpanCursor::Fallback { .. } => {
911 let n = self.nslots();
912 if n <= CAPS_INLINE_SLOTS {
913 let mut slots = [usize::MAX; CAPS_INLINE_SLOTS];
914 self.write_slots(&mut slots[..n]);
915 SlotStore::Inline { len: n as u8, slots }
916 } else {
917 let mut v = vec![usize::MAX; n];
918 self.write_slots(&mut v);
919 SlotStore::Spilled(v.into_boxed_slice())
920 }
921 }
922 }
923 }
924
925 fn copy_slots_into(&self, locs: &mut CaptureLocations) {
927 let ngroups = self.nslots() / 2;
928 locs.ensure(ngroups);
929 self.write_slots(&mut locs.slots);
930 }
931}
932
933#[derive(Clone, Debug)]
940pub struct CaptureLocations {
941 slots: Vec<usize>, ngroups: usize,
943}
944
945impl CaptureLocations {
946 pub fn len(&self) -> usize {
948 self.ngroups
949 }
950
951 pub fn is_empty(&self) -> bool {
953 self.ngroups == 0
954 }
955
956 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
959 if i >= self.ngroups {
960 return None;
961 }
962 let a = self.slots[2 * i];
963 let b = self.slots[2 * i + 1];
964 if a == usize::MAX {
965 None
966 } else {
967 Some((a, b))
968 }
969 }
970
971 fn ensure(&mut self, ngroups: usize) {
972 if self.ngroups != ngroups || self.slots.len() != 2 * ngroups {
973 self.slots.resize(2 * ngroups, 0);
974 self.ngroups = ngroups;
975 }
976 }
977}
978
979#[derive(Clone, Copy, Debug, PartialEq, Eq)]
981pub struct Match<'t> {
982 text: &'t str,
983 start: usize,
984 end: usize,
985}
986
987impl<'t> Match<'t> {
988 pub fn start(&self) -> usize {
990 self.start
991 }
992
993 pub fn end(&self) -> usize {
995 self.end
996 }
997
998 pub fn range(&self) -> std::ops::Range<usize> {
1000 self.start..self.end
1001 }
1002
1003 pub fn as_str(&self) -> &'t str {
1005 &self.text[self.start..self.end]
1006 }
1007
1008 pub fn is_empty(&self) -> bool {
1010 self.start == self.end
1011 }
1012
1013 pub fn len(&self) -> usize {
1015 self.end - self.start
1016 }
1017}
1018
1019pub struct Captures<'t> {
1021 text: &'t str,
1022 slots: SlotStore,
1023 groups: Arc<GroupInfo>,
1024}
1025
1026impl<'t> Captures<'t> {
1027 pub fn get(&self, i: usize) -> Option<Match<'t>> {
1029 self.slots.group(i).map(|(s, e)| Match { text: self.text, start: s, end: e })
1030 }
1031
1032 pub fn name(&self, name: &str) -> Option<Match<'t>> {
1034 self.groups.by_name.get(name).and_then(|&i| self.get(i))
1035 }
1036
1037 pub fn len(&self) -> usize {
1039 self.slots.ngroups()
1040 }
1041
1042 pub fn is_empty(&self) -> bool {
1044 self.slots.ngroups() == 0
1045 }
1046
1047 pub fn iter(&self) -> impl Iterator<Item = Option<Match<'t>>> + '_ {
1049 (0..self.len()).map(move |i| self.get(i))
1050 }
1051}
1052
1053impl Index<usize> for Captures<'_> {
1055 type Output = str;
1056 fn index(&self, i: usize) -> &str {
1057 self.get(i).map(|m| m.as_str()).unwrap_or_else(|| panic!("no group at index {i}"))
1058 }
1059}
1060
1061impl Index<&str> for Captures<'_> {
1062 type Output = str;
1063 fn index(&self, name: &str) -> &str {
1064 self.name(name).map(|m| m.as_str()).unwrap_or_else(|| panic!("no group named {name:?}"))
1065 }
1066}
1067
1068pub struct Matches<'r, 't> {
1070 raw: SpanCursor<'r, 't>,
1071 text: &'t str,
1072}
1073
1074impl<'t> Iterator for Matches<'_, 't> {
1075 type Item = Match<'t>;
1076 fn next(&mut self) -> Option<Match<'t>> {
1077 self.raw.advance().map(|(a, b)| Match { text: self.text, start: a, end: b })
1078 }
1079}
1080
1081pub struct CaptureMatches<'r, 't> {
1083 raw: SpanCursor<'r, 't>,
1084 re: &'r Regex,
1085 text: &'t str,
1086}
1087
1088pub struct CaptureLocationMatches<'r, 't> {
1093 raw: SpanCursor<'r, 't>,
1094 text: &'t str,
1095 ngroups: usize,
1096}
1097
1098impl CaptureLocationMatches<'_, '_> {
1099 pub fn len(&self) -> usize {
1101 self.ngroups
1102 }
1103
1104 pub fn is_empty(&self) -> bool {
1106 self.ngroups == 0
1107 }
1108
1109 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
1112 if i >= self.ngroups {
1113 return None;
1114 }
1115 match &self.raw {
1116 SpanCursor::Real(r) => {
1117 let a = r.buf[2 * i];
1118 let b = r.buf[2 * i + 1];
1119 if a == usize::MAX {
1120 None
1121 } else {
1122 Some((a, b))
1123 }
1124 }
1125 #[cfg(feature = "fallback")]
1126 SpanCursor::Fallback { cur, .. } => cur.get(i).copied().flatten(),
1127 }
1128 }
1129
1130 pub fn read_captures(&self, locs: &mut CaptureLocations) {
1132 self.raw.copy_slots_into(locs);
1133 }
1134}
1135
1136impl<'t> Iterator for CaptureLocationMatches<'_, 't> {
1137 type Item = Match<'t>;
1138 fn next(&mut self) -> Option<Match<'t>> {
1139 let (a, b) = self.raw.advance()?;
1140 Some(Match {
1141 text: self.text,
1142 start: a,
1143 end: b,
1144 })
1145 }
1146}
1147
1148impl<'t> Iterator for CaptureMatches<'_, 't> {
1149 type Item = Captures<'t>;
1150 fn next(&mut self) -> Option<Captures<'t>> {
1151 self.raw.advance().map(|_| self.re.caps_from(self.text, &self.raw))
1152 }
1153}
1154
1155const FLAG_ICASE: u32 = 1;
1157const FLAG_MULTILINE: u32 = 2;
1158const FLAG_DOTALL: u32 = 4;
1159const FLAG_VERBOSE: u32 = 16;
1160const FLAG_ASCII: u32 = 64;
1161
1162pub struct RegexBuilder {
1164 pattern: String,
1165 flags: u32,
1166 #[cfg(feature = "fallback")]
1167 fallback: bool,
1168}
1169
1170impl RegexBuilder {
1171 pub fn new(pattern: &str) -> RegexBuilder {
1173 RegexBuilder {
1174 pattern: pattern.to_string(),
1175 flags: 0,
1176 #[cfg(feature = "fallback")]
1177 fallback: false,
1178 }
1179 }
1180
1181 fn set(&mut self, bit: u32, yes: bool) -> &mut RegexBuilder {
1182 if yes { self.flags |= bit } else { self.flags &= !bit }
1183 self
1184 }
1185
1186 #[cfg(feature = "fallback")]
1190 pub fn fallback(&mut self, yes: bool) -> &mut RegexBuilder {
1191 self.fallback = yes;
1192 self
1193 }
1194
1195 pub fn case_insensitive(&mut self, yes: bool) -> &mut RegexBuilder {
1197 self.set(FLAG_ICASE, yes)
1198 }
1199
1200 pub fn multi_line(&mut self, yes: bool) -> &mut RegexBuilder {
1202 self.set(FLAG_MULTILINE, yes)
1203 }
1204
1205 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut RegexBuilder {
1207 self.set(FLAG_DOTALL, yes)
1208 }
1209
1210 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut RegexBuilder {
1212 self.set(FLAG_VERBOSE, yes)
1213 }
1214
1215 pub fn unicode(&mut self, yes: bool) -> &mut RegexBuilder {
1218 self.set(FLAG_ASCII, !yes)
1219 }
1220
1221 pub fn size_limit(&mut self, _bytes: usize) -> &mut RegexBuilder {
1224 self
1225 }
1226
1227 pub fn build(&self) -> Result<Regex, Error> {
1229 match Regex::with_flags(&self.pattern, self.flags) {
1230 Ok(re) => Ok(re),
1231 Err(e) => {
1232 #[cfg(feature = "fallback")]
1233 if self.fallback && e.is_unsupported() {
1234 return Regex::build_fallback(&self.pattern, self.flags);
1235 }
1236 Err(e)
1237 }
1238 }
1239 }
1240}
1241
1242use std::borrow::Cow;
1244
1245pub trait Replacer {
1249 fn replace_append(&mut self, caps: &Captures, dst: &mut String);
1251}
1252
1253pub struct NoExpand<'a>(pub &'a str);
1255
1256impl Replacer for NoExpand<'_> {
1257 fn replace_append(&mut self, _caps: &Captures, dst: &mut String) {
1258 dst.push_str(self.0);
1259 }
1260}
1261
1262impl Replacer for &str {
1263 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1264 expand(caps, self, dst);
1265 }
1266}
1267
1268impl Replacer for String {
1269 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1270 expand(caps, self, dst);
1271 }
1272}
1273
1274impl<F, T> Replacer for F
1275where
1276 F: FnMut(&Captures) -> T,
1277 T: AsRef<str>,
1278{
1279 fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
1280 dst.push_str((*self)(caps).as_ref());
1281 }
1282}
1283
1284fn expand(caps: &Captures, template: &str, dst: &mut String) {
1287 let mut rest = template;
1288 while let Some(i) = rest.find('$') {
1289 dst.push_str(&rest[..i]);
1290 rest = &rest[i + 1..];
1291 if let Some(stripped) = rest.strip_prefix('$') {
1292 dst.push('$');
1293 rest = stripped;
1294 continue;
1295 }
1296 let (name, after) = if let Some(braced) = rest.strip_prefix('{') {
1297 match braced.find('}') {
1298 Some(j) => (&braced[..j], &braced[j + 1..]),
1299 None => {
1300 dst.push('$');
1301 ("", rest)
1302 }
1303 }
1304 } else {
1305 let end = rest.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_')).unwrap_or(rest.len());
1306 (&rest[..end], &rest[end..])
1307 };
1308 rest = after;
1309 if name.is_empty() {
1310 dst.push('$');
1311 continue;
1312 }
1313 let m = match name.parse::<usize>() {
1314 Ok(n) => caps.get(n),
1315 Err(_) => caps.name(name),
1316 };
1317 if let Some(m) = m {
1318 dst.push_str(m.as_str());
1319 }
1320 }
1321 dst.push_str(rest);
1322}
1323
1324impl Regex {
1325 pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1328 self.replacen(text, 1, rep)
1329 }
1330
1331 pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
1333 self.replacen(text, 0, rep)
1334 }
1335
1336 pub fn replacen<'t, R: Replacer>(&self, text: &'t str, limit: usize, mut rep: R) -> Cow<'t, str> {
1338 let mut out: Option<String> = None;
1339 let mut last = 0;
1340 for (i, caps) in self.captures_iter(text).enumerate() {
1341 if limit != 0 && i >= limit {
1342 break;
1343 }
1344 let m = caps.get(0).unwrap();
1345 let dst = out.get_or_insert_with(|| String::with_capacity(text.len()));
1346 dst.push_str(&text[last..m.start()]);
1347 rep.replace_append(&caps, dst);
1348 last = m.end();
1349 }
1350 match out {
1351 Some(mut dst) => {
1352 dst.push_str(&text[last..]);
1353 Cow::Owned(dst)
1354 }
1355 None => Cow::Borrowed(text),
1356 }
1357 }
1358
1359 pub fn split<'r, 't>(&'r self, text: &'t str) -> Split<'r, 't> {
1362 Split { text, it: self.find_iter(text), last: 0, done: false }
1363 }
1364
1365 pub fn splitn<'r, 't>(&'r self, text: &'t str, limit: usize) -> SplitN<'r, 't> {
1368 SplitN { inner: self.split(text), limit, n: 0 }
1369 }
1370}
1371
1372pub struct Split<'r, 't> {
1374 text: &'t str,
1375 it: Matches<'r, 't>,
1376 last: usize,
1377 done: bool,
1378}
1379
1380impl<'t> Iterator for Split<'_, 't> {
1381 type Item = &'t str;
1382 fn next(&mut self) -> Option<&'t str> {
1383 if self.done {
1384 return None;
1385 }
1386 match self.it.next() {
1387 Some(m) => {
1388 let piece = &self.text[self.last..m.start()];
1389 self.last = m.end();
1390 Some(piece)
1391 }
1392 None => {
1393 self.done = true;
1394 Some(&self.text[self.last..])
1395 }
1396 }
1397 }
1398}
1399
1400pub struct SplitN<'r, 't> {
1402 inner: Split<'r, 't>,
1403 limit: usize,
1404 n: usize,
1405}
1406
1407impl<'t> Iterator for SplitN<'_, 't> {
1408 type Item = &'t str;
1409 fn next(&mut self) -> Option<&'t str> {
1410 if self.n >= self.limit {
1411 return None;
1412 }
1413 self.n += 1;
1414 if self.n == self.limit {
1415 if self.inner.done {
1417 return None;
1418 }
1419 self.inner.done = true;
1420 return Some(&self.inner.text[self.inner.last..]);
1421 }
1422 self.inner.next()
1423 }
1424}
1425
1426pub mod bytes {
1430 use super::{
1431 compile_handle, real_find_iter, real_find_iter_at, real_free, CaptureLocations, Error,
1432 GroupInfo, RawSpans, RealRegex, SlotStore, FLAG_ASCII, FLAG_DOTALL, FLAG_ICASE,
1433 FLAG_MULTILINE, FLAG_VERBOSE,
1434 };
1435 use std::borrow::Cow;
1436 use std::marker::PhantomData;
1437 use std::ops::Index;
1438 use std::os::raw::c_char;
1439 use std::sync::Arc;
1440
1441 const FLAG_BYTES: u32 = 8;
1442
1443 pub struct Regex {
1445 handle: *mut RealRegex,
1446 ngroups: usize,
1447 pattern: Vec<u8>,
1448 groups: Arc<GroupInfo>,
1449 }
1450
1451 unsafe impl Send for Regex {}
1452 unsafe impl Sync for Regex {}
1453
1454 impl Regex {
1455 pub fn new(pattern: &str) -> Result<Regex, Error> {
1457 Regex::with_flags(pattern.as_bytes(), 0)
1458 }
1459
1460 pub fn with_flags(pattern: &[u8], flags: u32) -> Result<Regex, Error> {
1462 let (handle, ngroups, groups) = compile_handle(pattern, flags | FLAG_BYTES)?;
1463 Ok(Regex { handle, ngroups, pattern: pattern.to_vec(), groups })
1464 }
1465
1466 pub fn as_bytes(&self) -> &[u8] {
1468 &self.pattern
1469 }
1470
1471 pub fn captures_len(&self) -> usize {
1473 self.ngroups
1474 }
1475
1476 pub fn capture_names(&self) -> impl Iterator<Item = Option<&str>> {
1478 self.groups.names.iter().map(|o| o.as_deref())
1479 }
1480
1481 fn raw<'r, 't>(&'r self, text: &'t [u8], start: Option<usize>) -> RawSpans<'r, 't> {
1482 let iter = unsafe {
1483 match start {
1484 None => real_find_iter(self.handle, text.as_ptr() as *const c_char, text.len()),
1485 Some(s) => real_find_iter_at(self.handle, text.as_ptr() as *const c_char, text.len(), s),
1486 }
1487 };
1488 assert!(!iter.is_null(), "real-regex: engine iteration failed");
1490 RawSpans { iter, handle: self.handle, text, ngroups: self.ngroups, buf: vec![0usize; 2 * self.ngroups], last_end: None, drive_pos: None, utf8: false, _re: PhantomData }
1491 }
1492
1493 fn caps_from<'t>(&self, text: &'t [u8], raw: &RawSpans<'_, '_>) -> Captures<'t> {
1494 Captures { text, slots: SlotStore::from_flat(&raw.buf), groups: Arc::clone(&self.groups) }
1496 }
1497
1498 pub fn is_match(&self, text: &[u8]) -> bool {
1500 self.raw(text, None).advance().is_some()
1501 }
1502
1503 pub fn find<'t>(&self, text: &'t [u8]) -> Option<Match<'t>> {
1505 self.raw(text, None).advance().map(|(a, b)| Match { text, start: a, end: b })
1506 }
1507
1508 pub fn find_at<'t>(&self, text: &'t [u8], start: usize) -> Option<Match<'t>> {
1510 self.raw(text, Some(start)).advance().map(|(a, b)| Match { text, start: a, end: b })
1511 }
1512
1513 pub fn find_iter<'r, 't>(&'r self, text: &'t [u8]) -> Matches<'r, 't> {
1515 Matches { raw: self.raw(text, None), text }
1516 }
1517
1518 pub fn is_match_at(&self, text: &[u8], start: usize) -> bool {
1520 self.raw(text, Some(start)).advance().is_some()
1521 }
1522
1523 pub fn captures<'t>(&self, text: &'t [u8]) -> Option<Captures<'t>> {
1525 {
1526 let mut c = self.raw(text, None);
1527 c.advance().map(|_| self.caps_from(text, &c))
1528 }
1529 }
1530
1531 pub fn captures_at<'t>(&self, text: &'t [u8], start: usize) -> Option<Captures<'t>> {
1533 {
1534 let mut c = self.raw(text, Some(start));
1535 c.advance().map(|_| self.caps_from(text, &c))
1536 }
1537 }
1538
1539 pub fn capture_locations(&self) -> CaptureLocations {
1541 CaptureLocations {
1542 slots: vec![0; 2 * self.ngroups],
1543 ngroups: self.ngroups,
1544 }
1545 }
1546
1547 pub fn captures_read<'t>(
1549 &self,
1550 locs: &mut CaptureLocations,
1551 text: &'t [u8],
1552 ) -> Option<Match<'t>> {
1553 self.captures_read_at(locs, text, 0)
1554 }
1555
1556 pub fn captures_read_at<'t>(
1558 &self,
1559 locs: &mut CaptureLocations,
1560 text: &'t [u8],
1561 start: usize,
1562 ) -> Option<Match<'t>> {
1563 locs.ensure(self.ngroups);
1564 let mut c = self.raw(text, if start == 0 { None } else { Some(start) });
1565 let (a, b) = c.advance()?;
1566 locs.slots.copy_from_slice(&c.buf);
1567 Some(Match {
1568 text,
1569 start: a,
1570 end: b,
1571 })
1572 }
1573
1574 pub fn captures_read_iter<'r, 't>(
1576 &'r self,
1577 text: &'t [u8],
1578 ) -> CaptureLocationMatches<'r, 't> {
1579 CaptureLocationMatches {
1580 raw: self.raw(text, None),
1581 text,
1582 ngroups: self.ngroups,
1583 }
1584 }
1585
1586 pub fn captures_iter<'r, 't>(&'r self, text: &'t [u8]) -> CaptureMatches<'r, 't> {
1588 CaptureMatches { raw: self.raw(text, None), re: self, text }
1589 }
1590
1591 pub fn shortest_match(&self, text: &[u8]) -> Option<usize> {
1594 self.raw(text, None).advance().map(|(_, e)| e)
1595 }
1596
1597 pub fn replace<'t, R: Replacer>(&self, text: &'t [u8], rep: R) -> Cow<'t, [u8]> {
1600 self.replacen(text, 1, rep)
1601 }
1602
1603 pub fn replace_all<'t, R: Replacer>(&self, text: &'t [u8], rep: R) -> Cow<'t, [u8]> {
1605 self.replacen(text, 0, rep)
1606 }
1607
1608 pub fn replacen<'t, R: Replacer>(&self, text: &'t [u8], limit: usize, mut rep: R) -> Cow<'t, [u8]> {
1610 let mut out: Option<Vec<u8>> = None;
1611 let mut last = 0;
1612 for (i, caps) in self.captures_iter(text).enumerate() {
1613 if limit != 0 && i >= limit {
1614 break;
1615 }
1616 let m = caps.get(0).unwrap();
1617 let dst = out.get_or_insert_with(|| Vec::with_capacity(text.len()));
1618 dst.extend_from_slice(&text[last..m.start()]);
1619 rep.replace_append(&caps, dst);
1620 last = m.end();
1621 }
1622 match out {
1623 Some(mut dst) => {
1624 dst.extend_from_slice(&text[last..]);
1625 Cow::Owned(dst)
1626 }
1627 None => Cow::Borrowed(text),
1628 }
1629 }
1630
1631 pub fn split<'r, 't>(&'r self, text: &'t [u8]) -> Split<'r, 't> {
1633 Split { text, it: self.find_iter(text), last: 0, done: false }
1634 }
1635
1636 pub fn splitn<'r, 't>(&'r self, text: &'t [u8], limit: usize) -> SplitN<'r, 't> {
1639 SplitN { inner: self.split(text), limit, n: 0 }
1640 }
1641 }
1642
1643 impl std::fmt::Display for Regex {
1644 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1646 match std::str::from_utf8(&self.pattern) {
1647 Ok(s) => f.write_str(s),
1648 Err(_) => f.write_str(&String::from_utf8_lossy(&self.pattern)),
1649 }
1650 }
1651 }
1652
1653 impl std::str::FromStr for Regex {
1654 type Err = Error;
1655
1656 fn from_str(s: &str) -> Result<Regex, Error> {
1657 Regex::new(s)
1658 }
1659 }
1660
1661 impl Drop for Regex {
1662 fn drop(&mut self) {
1663 unsafe { real_free(self.handle) }
1664 }
1665 }
1666
1667 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1669 pub struct Match<'t> {
1670 text: &'t [u8],
1671 start: usize,
1672 end: usize,
1673 }
1674
1675 impl<'t> Match<'t> {
1676 pub fn start(&self) -> usize { self.start }
1678 pub fn end(&self) -> usize { self.end }
1680 pub fn as_bytes(&self) -> &'t [u8] { &self.text[self.start..self.end] }
1682 pub fn range(&self) -> std::ops::Range<usize> { self.start..self.end }
1684 pub fn is_empty(&self) -> bool { self.start == self.end }
1686 pub fn len(&self) -> usize { self.end - self.start }
1688 }
1689
1690 pub struct Captures<'t> {
1692 text: &'t [u8],
1693 slots: SlotStore,
1694 groups: Arc<GroupInfo>,
1695 }
1696
1697 impl<'t> Captures<'t> {
1698 pub fn get(&self, i: usize) -> Option<Match<'t>> {
1700 self.slots.group(i).map(|(s, e)| Match { text: self.text, start: s, end: e })
1701 }
1702 pub fn name(&self, name: &str) -> Option<Match<'t>> {
1704 self.groups.by_name.get(name).and_then(|&i| self.get(i))
1705 }
1706 pub fn len(&self) -> usize { self.slots.ngroups() }
1708 pub fn is_empty(&self) -> bool { self.slots.ngroups() == 0 }
1710 }
1711
1712 impl Index<usize> for Captures<'_> {
1713 type Output = [u8];
1714 fn index(&self, i: usize) -> &[u8] {
1715 self.get(i).map(|m| m.as_bytes()).unwrap_or_else(|| panic!("no group at index {i}"))
1716 }
1717 }
1718
1719 impl Index<&str> for Captures<'_> {
1720 type Output = [u8];
1721 fn index(&self, name: &str) -> &[u8] {
1722 self.name(name).map(|m| m.as_bytes()).unwrap_or_else(|| panic!("no group named {name:?}"))
1723 }
1724 }
1725
1726 pub struct Matches<'r, 't> {
1728 raw: RawSpans<'r, 't>,
1729 text: &'t [u8],
1730 }
1731
1732 impl<'t> Iterator for Matches<'_, 't> {
1733 type Item = Match<'t>;
1734 fn next(&mut self) -> Option<Match<'t>> {
1735 self.raw.advance().map(|(a, b)| Match { text: self.text, start: a, end: b })
1736 }
1737 }
1738
1739 pub struct CaptureMatches<'r, 't> {
1741 raw: RawSpans<'r, 't>,
1742 re: &'r Regex,
1743 text: &'t [u8],
1744 }
1745
1746 pub struct CaptureLocationMatches<'r, 't> {
1748 raw: RawSpans<'r, 't>,
1749 text: &'t [u8],
1750 ngroups: usize,
1751 }
1752
1753 impl CaptureLocationMatches<'_, '_> {
1754 pub fn len(&self) -> usize {
1756 self.ngroups
1757 }
1758
1759 pub fn get(&self, i: usize) -> Option<(usize, usize)> {
1761 if i >= self.ngroups {
1762 return None;
1763 }
1764 let a = self.raw.buf[2 * i];
1765 let b = self.raw.buf[2 * i + 1];
1766 if a == usize::MAX {
1767 None
1768 } else {
1769 Some((a, b))
1770 }
1771 }
1772
1773 pub fn read_captures(&self, locs: &mut CaptureLocations) {
1775 locs.ensure(self.ngroups);
1776 locs.slots.copy_from_slice(&self.raw.buf);
1777 }
1778 }
1779
1780 impl<'t> Iterator for CaptureLocationMatches<'_, 't> {
1781 type Item = Match<'t>;
1782 fn next(&mut self) -> Option<Match<'t>> {
1783 let (a, b) = self.raw.advance()?;
1784 Some(Match {
1785 text: self.text,
1786 start: a,
1787 end: b,
1788 })
1789 }
1790 }
1791
1792 impl<'t> Iterator for CaptureMatches<'_, 't> {
1793 type Item = Captures<'t>;
1794 fn next(&mut self) -> Option<Captures<'t>> {
1795 self.raw.advance().map(|_| self.re.caps_from(self.text, &self.raw))
1796 }
1797 }
1798
1799 pub struct Split<'r, 't> {
1801 text: &'t [u8],
1802 it: Matches<'r, 't>,
1803 last: usize,
1804 done: bool,
1805 }
1806
1807 impl<'t> Iterator for Split<'_, 't> {
1808 type Item = &'t [u8];
1809 fn next(&mut self) -> Option<&'t [u8]> {
1810 if self.done {
1811 return None;
1812 }
1813 match self.it.next() {
1814 Some(m) => {
1815 let piece = &self.text[self.last..m.start()];
1816 self.last = m.end();
1817 Some(piece)
1818 }
1819 None => {
1820 self.done = true;
1821 Some(&self.text[self.last..])
1822 }
1823 }
1824 }
1825 }
1826
1827 pub struct SplitN<'r, 't> {
1829 inner: Split<'r, 't>,
1830 limit: usize,
1831 n: usize,
1832 }
1833
1834 impl<'t> Iterator for SplitN<'_, 't> {
1835 type Item = &'t [u8];
1836 fn next(&mut self) -> Option<&'t [u8]> {
1837 if self.n >= self.limit {
1838 return None;
1839 }
1840 self.n += 1;
1841 if self.n == self.limit {
1842 if self.inner.done {
1843 return None;
1844 }
1845 self.inner.done = true;
1846 return Some(&self.inner.text[self.inner.last..]);
1847 }
1848 self.inner.next()
1849 }
1850 }
1851
1852 pub trait Replacer {
1855 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>);
1857 }
1858
1859 pub struct NoExpand<'a>(pub &'a [u8]);
1861
1862 impl Replacer for NoExpand<'_> {
1863 fn replace_append(&mut self, _caps: &Captures, dst: &mut Vec<u8>) {
1864 dst.extend_from_slice(self.0);
1865 }
1866 }
1867
1868 impl Replacer for &[u8] {
1869 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
1870 expand_bytes(caps, self, dst);
1871 }
1872 }
1873
1874 impl<F, T> Replacer for F
1875 where
1876 F: FnMut(&Captures) -> T,
1877 T: AsRef<[u8]>,
1878 {
1879 fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
1880 dst.extend_from_slice((*self)(caps).as_ref());
1881 }
1882 }
1883
1884 fn expand_bytes(caps: &Captures, template: &[u8], dst: &mut Vec<u8>) {
1887 let mut i = 0;
1888 while i < template.len() {
1889 let b = template[i];
1890 if b != b'$' {
1891 dst.push(b);
1892 i += 1;
1893 continue;
1894 }
1895 i += 1; if i < template.len() && template[i] == b'$' {
1897 dst.push(b'$');
1898 i += 1;
1899 continue;
1900 }
1901 let (name, next) = if i < template.len() && template[i] == b'{' {
1902 match template[i + 1..].iter().position(|&c| c == b'}') {
1903 Some(j) => (&template[i + 1..i + 1 + j], i + 1 + j + 1),
1904 None => {
1905 dst.push(b'$');
1906 continue;
1907 }
1908 }
1909 } else {
1910 let mut j = i;
1911 while j < template.len() && (template[j].is_ascii_alphanumeric() || template[j] == b'_') {
1912 j += 1;
1913 }
1914 (&template[i..j], j)
1915 };
1916 i = next;
1917 if name.is_empty() {
1918 dst.push(b'$');
1919 continue;
1920 }
1921 let name_str = std::str::from_utf8(name).unwrap_or("");
1922 let m = match name_str.parse::<usize>() {
1923 Ok(n) => caps.get(n),
1924 Err(_) => caps.name(name_str),
1925 };
1926 if let Some(m) = m {
1927 dst.extend_from_slice(m.as_bytes());
1928 }
1929 }
1930 }
1931
1932 pub struct RegexBuilder {
1934 pattern: Vec<u8>,
1935 flags: u32,
1936 }
1937
1938 impl RegexBuilder {
1939 pub fn new(pattern: &str) -> RegexBuilder {
1941 RegexBuilder { pattern: pattern.as_bytes().to_vec(), flags: 0 }
1942 }
1943 fn set(&mut self, bit: u32, yes: bool) -> &mut RegexBuilder {
1944 if yes { self.flags |= bit } else { self.flags &= !bit }
1945 self
1946 }
1947 pub fn case_insensitive(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_ICASE, yes) }
1949 pub fn multi_line(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_MULTILINE, yes) }
1951 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_DOTALL, yes) }
1953 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_VERBOSE, yes) }
1955 pub fn unicode(&mut self, yes: bool) -> &mut RegexBuilder { self.set(FLAG_ASCII, !yes) }
1957 pub fn size_limit(&mut self, _bytes: usize) -> &mut RegexBuilder { self }
1959 pub fn build(&self) -> Result<Regex, Error> {
1961 Regex::with_flags(&self.pattern, self.flags)
1962 }
1963 }
1964}