1#![allow(
14 missing_docs,
15 non_snake_case,
16 non_camel_case_types,
17 non_upper_case_globals
18)]
19
20use core::ffi::c_void;
21use core::ptr;
22use std::os::raw::c_int;
23
24use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl};
25use crate::abi::types::xmlChar;
26
27#[allow(dead_code)]
33const MAX_NFA_STATES: usize = 1024;
34
35#[allow(dead_code)]
37const MAX_PARSE_DEPTH: usize = 256;
38
39const REGEXP_MATCH: c_int = 1;
41
42const REGEXP_NOMATCH: c_int = 0;
44
45const REGEXP_ERROR: c_int = -1;
47
48#[derive(Debug, Clone, Copy, PartialEq)]
54enum AnchorType {
55 Start,
57 End,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq)]
63enum PredefinedClass {
64 Digit,
66 NotDigit,
68 Space,
70 NotSpace,
72 Word,
74 NotWord,
76}
77
78#[derive(Debug, Clone, PartialEq)]
80enum Transition {
81 Char(u8),
83 Range(u8, u8),
85 #[allow(dead_code)]
87 Set(Vec<u8>),
88 NotRange(u8, u8),
90 NotSet(Vec<u8>),
92 Wildcard,
94 Predefined(PredefinedClass),
96 Epsilon,
98 Anchor(AnchorType),
100}
101
102#[derive(Debug, Clone)]
108struct NfaState {
109 transitions: Vec<(Transition, usize)>,
111 is_accept: bool,
113}
114
115impl NfaState {
116 const fn new() -> Self {
117 NfaState {
118 transitions: Vec::new(),
119 is_accept: false,
120 }
121 }
122}
123
124#[derive(Debug, Clone)]
126struct Nfa {
127 states: Vec<NfaState>,
129 start: usize,
131}
132
133impl Nfa {
134 fn new() -> Self {
135 let start = 0;
136 Nfa {
137 states: vec![NfaState::new()],
138 start,
139 }
140 }
141
142 fn add_state(&mut self) -> usize {
144 let index = self.states.len();
145 self.states.push(NfaState::new());
146 index
147 }
148
149 fn add_transition(&mut self, from: usize, to: usize, trans: Transition) {
151 if from < self.states.len() && to < self.states.len() {
152 self.states[from].transitions.push((trans, to));
153 }
154 }
155
156 fn set_accept(&mut self, state: usize) {
158 if state < self.states.len() {
159 self.states[state].is_accept = true;
160 }
161 }
162}
163
164struct NfaFragment {
169 nfa: Nfa,
170 start: usize,
172 out: Vec<usize>,
177}
178
179impl NfaFragment {
180 const fn new(nfa: Nfa, start: usize, out: Vec<usize>) -> Self {
181 NfaFragment { nfa, start, out }
182 }
183}
184
185impl Clone for NfaFragment {
186 fn clone(&self) -> Self {
187 NfaFragment {
188 nfa: self.nfa.clone(),
189 start: self.start,
190 out: self.out.clone(),
191 }
192 }
193}
194
195fn nfa_char(c: u8) -> NfaFragment {
201 let mut nfa = Nfa::new();
202 let start = nfa.start;
203 let accept = nfa.add_state();
204 nfa.set_accept(accept);
205 nfa.add_transition(start, accept, Transition::Char(c));
206 NfaFragment::new(nfa, start, vec![accept])
207}
208
209fn nfa_epsilon() -> NfaFragment {
211 let mut nfa = Nfa::new();
212 let start = nfa.start;
213 nfa.set_accept(start);
214 NfaFragment::new(nfa, start, vec![start])
215}
216
217fn nfa_range(lo: u8, hi: u8) -> NfaFragment {
219 let mut nfa = Nfa::new();
220 let start = nfa.start;
221 let accept = nfa.add_state();
222 nfa.set_accept(accept);
223 nfa.add_transition(start, accept, Transition::Range(lo, hi));
224 NfaFragment::new(nfa, start, vec![accept])
225}
226
227#[allow(dead_code)]
229fn nfa_set(chars: Vec<u8>) -> NfaFragment {
230 let mut nfa = Nfa::new();
231 let start = nfa.start;
232 let accept = nfa.add_state();
233 nfa.set_accept(accept);
234 nfa.add_transition(start, accept, Transition::Set(chars));
235 NfaFragment::new(nfa, start, vec![accept])
236}
237
238fn nfa_not_range(lo: u8, hi: u8) -> NfaFragment {
240 let mut nfa = Nfa::new();
241 let start = nfa.start;
242 let accept = nfa.add_state();
243 nfa.set_accept(accept);
244 nfa.add_transition(start, accept, Transition::NotRange(lo, hi));
245 NfaFragment::new(nfa, start, vec![accept])
246}
247
248#[allow(dead_code)]
250fn nfa_not_set(chars: Vec<u8>) -> NfaFragment {
251 let mut nfa = Nfa::new();
252 let start = nfa.start;
253 let accept = nfa.add_state();
254 nfa.set_accept(accept);
255 nfa.add_transition(start, accept, Transition::NotSet(chars));
256 NfaFragment::new(nfa, start, vec![accept])
257}
258
259fn nfa_wildcard() -> NfaFragment {
261 let mut nfa = Nfa::new();
262 let start = nfa.start;
263 let accept = nfa.add_state();
264 nfa.set_accept(accept);
265 nfa.add_transition(start, accept, Transition::Wildcard);
266 NfaFragment::new(nfa, start, vec![accept])
267}
268
269fn nfa_predefined(class: PredefinedClass) -> NfaFragment {
271 let mut nfa = Nfa::new();
272 let start = nfa.start;
273 let accept = nfa.add_state();
274 nfa.set_accept(accept);
275 nfa.add_transition(start, accept, Transition::Predefined(class));
276 NfaFragment::new(nfa, start, vec![accept])
277}
278
279fn nfa_start_anchor() -> NfaFragment {
281 let mut nfa = Nfa::new();
282 let start = nfa.start;
283 let accept = nfa.add_state();
284 nfa.set_accept(accept);
285 nfa.add_transition(start, accept, Transition::Anchor(AnchorType::Start));
286 NfaFragment::new(nfa, start, vec![accept])
287}
288
289fn nfa_end_anchor() -> NfaFragment {
291 let mut nfa = Nfa::new();
292 let start = nfa.start;
293 let accept = nfa.add_state();
294 nfa.set_accept(accept);
295 nfa.add_transition(start, accept, Transition::Anchor(AnchorType::End));
296 NfaFragment::new(nfa, start, vec![accept])
297}
298
299fn concat(a: NfaFragment, b: NfaFragment) -> NfaFragment {
304 let a_out = a.out.clone();
305 let a_start = a.start;
306 let a_size = a.nfa.states.len();
307
308 let mut nfa = a.nfa;
309 let b_start = b.nfa.start + a_size;
310
311 for mut state in b.nfa.states {
313 for (_, target) in &mut state.transitions {
314 *target += a_size;
315 }
316 nfa.states.push(state);
317 }
318
319 for &out_state in &a_out {
321 nfa.add_transition(out_state, b_start, Transition::Epsilon);
322 }
323
324 let b_out: Vec<usize> = b.out.iter().map(|&s| s + a_size).collect();
326
327 NfaFragment::new(nfa, a_start, b_out)
328}
329
330fn union(a: NfaFragment, b: NfaFragment) -> NfaFragment {
334 let mut nfa = Nfa::new();
335 let new_start = nfa.start;
336
337 let a_start = nfa.states.len();
339 let _a_size = a.nfa.states.len();
340 for mut state in a.nfa.states {
341 for (_, target) in &mut state.transitions {
342 *target += a_start;
343 }
344 nfa.states.push(state);
345 }
346
347 let b_start = nfa.states.len();
349 for mut state in b.nfa.states {
350 for (_, target) in &mut state.transitions {
351 *target += b_start;
352 }
353 nfa.states.push(state);
354 }
355
356 nfa.add_transition(new_start, a_start, Transition::Epsilon);
358 nfa.add_transition(new_start, b_start, Transition::Epsilon);
359
360 let mut out = Vec::new();
362 for &s in &a.out {
363 out.push(s + a_start);
364 }
365 for &s in &b.out {
366 out.push(s + b_start);
367 }
368
369 NfaFragment::new(nfa, new_start, out)
370}
371
372fn kleene_star(frag: NfaFragment) -> NfaFragment {
374 let mut nfa = Nfa::new();
375 let new_start = nfa.start;
376 let new_accept = nfa.add_state();
377
378 let frag_start = nfa.states.len();
380 let _frag_size = frag.nfa.states.len();
381 for mut state in frag.nfa.states {
382 for (_, target) in &mut state.transitions {
383 *target += frag_start;
384 }
385 nfa.states.push(state);
386 }
387
388 nfa.add_transition(new_start, new_accept, Transition::Epsilon);
390 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
391
392 for &s in &frag.out {
394 nfa.add_transition(s + frag_start, frag_start, Transition::Epsilon);
395 nfa.add_transition(s + frag_start, new_accept, Transition::Epsilon);
396 }
397
398 nfa.set_accept(new_accept);
399 NfaFragment::new(nfa, new_start, vec![new_accept])
400}
401
402fn plus(frag: NfaFragment) -> NfaFragment {
404 let out_orig = frag.out.clone();
405 let _frag_start_orig = frag.start;
406
407 let mut nfa = Nfa::new();
408 let new_start = nfa.start;
409
410 let frag_start = nfa.states.len();
412 for mut state in frag.nfa.states {
413 for (_, target) in &mut state.transitions {
414 *target += frag_start;
415 }
416 nfa.states.push(state);
417 }
418
419 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
421
422 for &s in &out_orig {
424 let adjusted = s + frag_start;
425 nfa.add_transition(adjusted, frag_start, Transition::Epsilon);
426 }
427
428 let out: Vec<usize> = out_orig.iter().map(|&s| s + frag_start).collect();
430
431 NfaFragment::new(nfa, new_start, out)
432}
433
434fn optional(frag: NfaFragment) -> NfaFragment {
436 let mut nfa = Nfa::new();
437 let new_start = nfa.start;
438 let new_accept = nfa.add_state();
439
440 let frag_start = nfa.states.len();
441 for mut state in frag.nfa.states {
442 for (_, target) in &mut state.transitions {
443 *target += frag_start;
444 }
445 nfa.states.push(state);
446 }
447
448 nfa.add_transition(new_start, new_accept, Transition::Epsilon);
450 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
451
452 for &s in &frag.out {
454 nfa.add_transition(s + frag_start, new_accept, Transition::Epsilon);
455 }
456
457 nfa.set_accept(new_accept);
458 NfaFragment::new(nfa, new_start, vec![new_accept])
459}
460
461#[derive(Debug, Clone, PartialEq)]
467#[allow(dead_code)]
468enum RegexToken {
469 Char(u8),
471 Dot,
473 AnchorStart,
475 AnchorEnd,
477 LParen,
479 RParen,
481 Pipe,
483 Star,
485 Plus,
487 Question,
489 LBrace,
491 RBrace,
493 Comma,
495 #[allow(dead_code)]
497 Number(u32),
498 Escape(u8),
500 ClassStart,
502 ClassEnd,
504 ClassNegate,
506 ClassRange,
508}
509
510struct RegexParser<'a> {
512 input: &'a [u8],
514 pos: usize,
516 lookahead: Option<RegexToken>,
518}
519
520impl<'a> RegexParser<'a> {
521 const fn new(input: &'a [u8]) -> Self {
522 RegexParser {
523 input,
524 pos: 0,
525 lookahead: None,
526 }
527 }
528
529 fn peek(&self) -> Option<u8> {
531 self.input.get(self.pos).copied()
532 }
533
534 fn advance(&mut self) -> Option<u8> {
536 let ch = self.input.get(self.pos).copied();
537 if ch.is_some() {
538 self.pos += 1;
539 }
540 ch
541 }
542
543 #[allow(dead_code)]
545 fn skip_whitespace(&mut self) {
546 while let Some(ch) = self.peek() {
547 if ch == b' ' || ch == b'\t' || ch == b'\n' || ch == b'\r' {
548 self.advance();
549 } else {
550 break;
551 }
552 }
553 }
554
555 fn scan_token(&mut self) -> Option<RegexToken> {
557 let ch = self.advance()?;
558 match ch {
559 b'.' => Some(RegexToken::Dot),
560 b'^' => Some(RegexToken::AnchorStart),
561 b'$' => Some(RegexToken::AnchorEnd),
562 b'(' => Some(RegexToken::LParen),
563 b')' => Some(RegexToken::RParen),
564 b'|' => Some(RegexToken::Pipe),
565 b'*' => Some(RegexToken::Star),
566 b'+' => Some(RegexToken::Plus),
567 b'?' => Some(RegexToken::Question),
568 b'{' => Some(RegexToken::LBrace),
569 b'}' => Some(RegexToken::RBrace),
570 b',' => Some(RegexToken::Comma),
571 b'[' => Some(RegexToken::ClassStart),
572 b']' => Some(RegexToken::ClassEnd),
573 b'\\' => {
574 let next = self.advance()?;
576 Some(RegexToken::Escape(next))
577 }
578 _ => Some(RegexToken::Char(ch)),
579 }
580 }
581
582 fn next_token(&mut self) -> Option<RegexToken> {
584 if let Some(token) = self.lookahead.take() {
585 Some(token)
586 } else {
587 self.scan_token()
588 }
589 }
590
591 const fn unscan(&mut self, token: RegexToken) {
593 self.lookahead = Some(token);
594 }
595
596 fn parse(&mut self) -> Result<NfaFragment, String> {
600 let frag = self.parse_alternation()?;
601 Ok(frag)
602 }
603
604 fn parse_alternation(&mut self) -> Result<NfaFragment, String> {
606 let mut frag = self.parse_sequence()?;
607
608 loop {
609 match self.next_token() {
610 Some(RegexToken::Pipe) => {
611 let rhs = self.parse_sequence()?;
612 frag = union(frag, rhs);
613 }
614 Some(other) => {
615 self.unscan(other);
616 break;
617 }
618 None => break,
619 }
620 }
621
622 Ok(frag)
623 }
624
625 fn parse_sequence(&mut self) -> Result<NfaFragment, String> {
627 let mut fragments: Vec<NfaFragment> = Vec::new();
628
629 loop {
630 match self.peek() {
631 None => break,
632 Some(b'|') | Some(b')') => break,
633 _ => {}
634 }
635
636 let atom = self.parse_atom()?;
637 fragments.push(atom);
638 }
639
640 if fragments.is_empty() {
641 return Ok(nfa_epsilon());
642 }
643
644 let mut result = fragments.remove(0);
645 for frag in fragments {
646 result = concat(result, frag);
647 }
648
649 Ok(result)
650 }
651
652 fn parse_atom(&mut self) -> Result<NfaFragment, String> {
654 let token = self
655 .next_token()
656 .ok_or_else(|| "Unexpected end of pattern".to_string())?;
657
658 let base = match token {
659 RegexToken::Char(c) => nfa_char(c),
660 RegexToken::Dot => nfa_wildcard(),
661 RegexToken::AnchorStart => nfa_start_anchor(),
662 RegexToken::AnchorEnd => nfa_end_anchor(),
663 RegexToken::LParen => {
664 let inner = self.parse_alternation()?;
665 match self.next_token() {
666 Some(RegexToken::RParen) => inner,
667 Some(t) => return Err(format!("Expected ')', got {:?}", t)),
668 None => return Err("Unterminated group".to_string()),
669 }
670 }
671 RegexToken::Escape(c) => {
672 match c {
673 b'd' => nfa_predefined(PredefinedClass::Digit),
674 b'D' => nfa_predefined(PredefinedClass::NotDigit),
675 b's' => nfa_predefined(PredefinedClass::Space),
676 b'S' => nfa_predefined(PredefinedClass::NotSpace),
677 b'w' => nfa_predefined(PredefinedClass::Word),
678 b'W' => nfa_predefined(PredefinedClass::NotWord),
679 b'n' => nfa_char(b'\n'),
680 b'r' => nfa_char(b'\r'),
681 b't' => nfa_char(b'\t'),
682 b'\\' => nfa_char(b'\\'),
683 b'.' => nfa_char(b'.'),
684 b'^' => nfa_char(b'^'),
685 b'$' => nfa_char(b'$'),
686 b'|' => nfa_char(b'|'),
687 b'*' => nfa_char(b'*'),
688 b'+' => nfa_char(b'+'),
689 b'?' => nfa_char(b'?'),
690 b'(' => nfa_char(b'('),
691 b')' => nfa_char(b')'),
692 b'[' => nfa_char(b'['),
693 b']' => nfa_char(b']'),
694 b'{' => nfa_char(b'{'),
695 b'}' => nfa_char(b'}'),
696 b'-' => nfa_char(b'-'),
697 b'0'..=b'9' => {
698 nfa_char(c)
700 }
701 _ => nfa_char(c),
702 }
703 }
704 RegexToken::ClassStart => self.parse_char_class()?,
705 _ => return Err(format!("Unexpected token: {:?}", token)),
706 };
707
708 self.parse_quantifier(base)
710 }
711
712 fn parse_quantifier(&mut self, frag: NfaFragment) -> Result<NfaFragment, String> {
714 match self.peek() {
715 Some(b'*') => {
716 self.advance();
717 Ok(kleene_star(frag))
718 }
719 Some(b'+') => {
720 self.advance();
721 Ok(plus(frag))
722 }
723 Some(b'?') => {
724 self.advance();
725 Ok(optional(frag))
726 }
727 Some(b'{') => {
728 self.advance();
729 self.parse_brace_quantifier(frag)
730 }
731 _ => Ok(frag),
732 }
733 }
734
735 fn parse_brace_quantifier(&mut self, frag: NfaFragment) -> Result<NfaFragment, String> {
737 let mut min: u32 = 0;
739 while let Some(b'0'..=b'9') = self.peek() {
740 let d = self.advance().unwrap() - b'0';
741 min = min * 10 + d as u32;
742 }
743
744 let mut max: Option<u32> = None;
745
746 match self.peek() {
747 Some(b',') => {
748 self.advance();
749 let mut max_val: u32 = 0;
751 let mut has_max = false;
752 while let Some(b'0'..=b'9') = self.peek() {
753 let d = self.advance().unwrap() - b'0';
754 max_val = max_val * 10 + d as u32;
755 has_max = true;
756 }
757 if has_max {
758 max = Some(max_val);
759 }
760 }
761 Some(b'}') => {
762 max = Some(min);
763 }
764 _ => return Err("Expected '}' in quantifier".to_string()),
765 }
766
767 match self.peek() {
769 Some(b'}') => {
770 self.advance();
771 }
772 _ => return Err("Expected '}' in quantifier".to_string()),
773 }
774
775 if min == 0 && max.is_none() {
780 Ok(kleene_star(frag))
782 } else if min == 0 && max == Some(0) {
783 Ok(nfa_epsilon())
785 } else if min == 1 && max.is_none() {
786 Ok(plus(frag))
788 } else if min == 0 && max == Some(1) {
789 Ok(optional(frag))
791 } else {
792 let mut result = nfa_epsilon();
795 for _ in 0..min {
796 result = concat(result, frag.clone());
797 }
798 if let Some(max_val) = max {
799 for _ in min..max_val {
800 result = concat(result, optional(frag.clone()));
801 }
802 } else {
803 result = concat(result, kleene_star(frag.clone()));
805 }
806 Ok(result)
807 }
808 }
809
810 fn parse_char_class(&mut self) -> Result<NfaFragment, String> {
812 let mut chars: Vec<u8> = Vec::new();
813 let mut ranges: Vec<(u8, u8)> = Vec::new();
814 let mut negated = false;
815
816 if let Some(b'^') = self.peek() {
818 negated = true;
819 self.advance();
820 }
821
822 let mut prev: Option<u8> = None;
824
825 loop {
826 match self.peek() {
827 None => return Err("Unterminated character class".to_string()),
828 Some(b']') => {
829 if prev.is_some() {
830 if let Some(c) = prev {
832 chars.push(c);
833 }
834 prev = None;
835 }
836 self.advance();
837 break;
838 }
839 Some(b'-') if prev.is_some() => {
840 self.advance();
842 let lo = prev.take().unwrap();
843 match self.peek() {
844 Some(b']') => {
845 chars.push(lo);
847 chars.push(b'-');
848 prev = None;
849 }
850 Some(ch) => {
851 self.advance();
852 if lo <= ch {
853 ranges.push((lo, ch));
854 }
855 prev = None;
856 }
857 None => {
858 chars.push(lo);
859 chars.push(b'-');
860 prev = None;
861 }
862 }
863 }
864 Some(b'\\') => {
865 self.advance();
866 if let Some(ch) = self.advance() {
867 match ch {
871 b'd' | b'D' | b's' | b'S' | b'w' | b'W' => {
872 if let Some(p) = prev.take() {
874 chars.push(p);
875 }
876 let class = match ch {
880 b'd' => PredefinedClass::Digit,
881 b'D' => PredefinedClass::NotDigit,
882 b's' => PredefinedClass::Space,
883 b'S' => PredefinedClass::NotSpace,
884 b'w' => PredefinedClass::Word,
885 b'W' => PredefinedClass::NotWord,
886 _ => unreachable!(),
887 };
888 match class {
893 PredefinedClass::Digit => {
894 ranges.push((b'0', b'9'));
895 }
896 PredefinedClass::NotDigit => {
897 ranges.push((0x00, b'/' - 1));
898 ranges.push((b':', 0xFF));
899 }
900 PredefinedClass::Space => {
901 chars.push(b' ');
902 chars.push(b'\t');
903 chars.push(b'\n');
904 chars.push(b'\r');
905 }
906 PredefinedClass::NotSpace => {
907 ranges.push((0x00, b' ' - 1));
909 ranges.push((b'!' + 1, b'\t' - 1));
910 ranges.push((b'\t' + 1, b'\n' - 1));
911 ranges.push((b'\n' + 1, b'\r' - 1));
912 ranges.push((b'\r' + 1, 0xFF));
913 }
914 PredefinedClass::Word => {
915 ranges.push((b'0', b'9'));
916 ranges.push((b'A', b'Z'));
917 ranges.push((b'a', b'z'));
918 chars.push(b'_');
919 }
920 PredefinedClass::NotWord => {
921 ranges.push((0x00, b'0' - 1));
922 ranges.push((b'9' + 1, b'A' - 1));
923 ranges.push((b'Z' + 1, b'_' - 1));
924 ranges.push((b'_' + 1, b'a' - 1));
925 ranges.push((b'z' + 1, 0xFF));
926 }
927 }
928 }
929 _ => {
930 let c = match ch {
931 b'n' => b'\n',
932 b'r' => b'\r',
933 b't' => b'\t',
934 b'\\' => b'\\',
935 b'0'..=b'9' => ch, _ => ch,
937 };
938 if let Some(p) = prev.take() {
939 chars.push(p);
940 }
941 prev = Some(c);
942 }
943 }
944 }
945 }
946 Some(ch) => {
947 self.advance();
948 if let Some(p) = prev.take() {
949 chars.push(p);
950 }
951 prev = Some(ch);
952 }
953 }
954 }
955
956 if let Some(c) = prev {
958 chars.push(c);
959 }
960
961 let mut combined_ranges: Vec<(u8, u8)> = ranges;
963 for &c in &chars {
964 combined_ranges.push((c, c));
965 }
966
967 if combined_ranges.is_empty() {
968 return if negated {
969 Ok(nfa_epsilon())
971 } else {
972 Ok(nfa_epsilon())
974 };
975 }
976
977 combined_ranges.sort_by_key(|a| a.0);
979 let mut merged: Vec<(u8, u8)> = Vec::new();
980 for (lo, hi) in combined_ranges {
981 if let Some(last) = merged.last_mut() {
982 if lo <= last.1 + 1 {
983 last.1 = last.1.max(hi);
984 continue;
985 }
986 }
987 merged.push((lo, hi));
988 }
989
990 if negated {
992 if merged.len() == 1 && merged[0].0 == 0 && merged[0].1 == 255 {
993 Ok(nfa_epsilon())
995 } else {
996 Ok(nfa_not_range(0, 255))
997 }
998 } else if merged.len() == 1 {
999 let (lo, hi) = merged[0];
1000 if lo == hi {
1001 Ok(nfa_char(lo))
1002 } else {
1003 Ok(nfa_range(lo, hi))
1004 }
1005 } else {
1006 let mut result = nfa_range(merged[0].0, merged[0].1);
1008 for &(lo, hi) in &merged[1..] {
1009 result = union(result, nfa_range(lo, hi));
1010 }
1011 Ok(result)
1012 }
1013 }
1014}
1015
1016fn matches_transition(c: u8, trans: &Transition) -> bool {
1022 match trans {
1023 Transition::Char(ch) => c == *ch,
1024 Transition::Range(lo, hi) => c >= *lo && c <= *hi,
1025 Transition::Set(chars) => chars.contains(&c),
1026 Transition::NotRange(lo, hi) => c < *lo || c > *hi,
1027 Transition::NotSet(chars) => !chars.contains(&c),
1028 Transition::Wildcard => true,
1029 Transition::Predefined(class) => matches_predefined(c, *class),
1030 Transition::Epsilon => false, Transition::Anchor(_) => false, }
1033}
1034
1035const fn matches_predefined(c: u8, class: PredefinedClass) -> bool {
1037 match class {
1038 PredefinedClass::Digit => c >= b'0' && c <= b'9',
1039 PredefinedClass::NotDigit => c < b'0' || c > b'9',
1040 PredefinedClass::Space => {
1041 c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0b || c == 0x0c
1042 }
1043 PredefinedClass::NotSpace => {
1044 !(c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0b || c == 0x0c)
1045 }
1046 PredefinedClass::Word => {
1047 (c >= b'0' && c <= b'9')
1048 || (c >= b'A' && c <= b'Z')
1049 || (c >= b'a' && c <= b'z')
1050 || c == b'_'
1051 }
1052 PredefinedClass::NotWord => {
1053 !((c >= b'0' && c <= b'9')
1054 || (c >= b'A' && c <= b'Z')
1055 || (c >= b'a' && c <= b'z')
1056 || c == b'_')
1057 }
1058 }
1059}
1060
1061fn epsilon_closure(nfa: &Nfa, states: &[usize]) -> Vec<usize> {
1063 let mut visited = vec![false; nfa.states.len()];
1064 let mut result = Vec::new();
1065 let mut stack: Vec<usize> = states.to_vec();
1066
1067 while let Some(s) = stack.pop() {
1068 if s >= nfa.states.len() || visited[s] {
1069 continue;
1070 }
1071 visited[s] = true;
1072 result.push(s);
1073
1074 for (cond, target) in &nfa.states[s].transitions {
1075 if matches!(cond, Transition::Epsilon)
1076 && *target < nfa.states.len()
1077 && !visited[*target]
1078 {
1079 stack.push(*target);
1080 }
1081 }
1082 }
1083
1084 result
1085}
1086
1087fn move_on_char(nfa: &Nfa, states: &[usize], c: u8, is_start: bool, is_end: bool) -> Vec<usize> {
1092 let mut expanded = states.to_vec();
1095 let mut more = true;
1096 while more {
1097 more = false;
1098 let mut new_states = Vec::new();
1099 for &s in &expanded {
1100 if s >= nfa.states.len() {
1101 continue;
1102 }
1103 for (cond, target) in &nfa.states[s].transitions {
1104 if *target >= nfa.states.len() {
1105 continue;
1106 }
1107 let should_follow = match cond {
1108 Transition::Anchor(AnchorType::Start) => {
1109 is_start && !expanded.contains(target) && !new_states.contains(target)
1110 }
1111 Transition::Anchor(AnchorType::End) => {
1112 is_end && !expanded.contains(target) && !new_states.contains(target)
1113 }
1114 _ => false,
1115 };
1116 if should_follow {
1117 new_states.push(*target);
1118 more = true;
1119 }
1120 }
1121 }
1122 expanded.extend(new_states);
1123 }
1124
1125 let mut eps_expanded = expanded.clone();
1129 let mut more_eps = true;
1130 while more_eps {
1131 more_eps = false;
1132 let mut new_eps = Vec::new();
1133 for &s in &eps_expanded {
1134 if s >= nfa.states.len() {
1135 continue;
1136 }
1137 for (cond, target) in &nfa.states[s].transitions {
1138 if let Transition::Epsilon = cond {
1139 if *target < nfa.states.len()
1140 && !eps_expanded.contains(target)
1141 && !new_eps.contains(target)
1142 {
1143 new_eps.push(*target);
1144 more_eps = true;
1145 }
1146 }
1147 }
1148 }
1149 eps_expanded.extend(new_eps);
1150 }
1151
1152 let mut next = Vec::new();
1154 for &s in &eps_expanded {
1155 if s >= nfa.states.len() {
1156 continue;
1157 }
1158 for (cond, target) in &nfa.states[s].transitions {
1159 if *target >= nfa.states.len() {
1160 continue;
1161 }
1162 match cond {
1163 Transition::Epsilon => continue,
1164 Transition::Anchor(_) => continue, _ => {
1166 if matches_transition(c, cond) && !next.contains(target) {
1167 next.push(*target);
1168 }
1169 }
1170 }
1171 }
1172 }
1173
1174 next
1175}
1176
1177fn has_accept_state(nfa: &Nfa, states: &[usize]) -> bool {
1179 states
1180 .iter()
1181 .any(|&s| s < nfa.states.len() && nfa.states[s].is_accept)
1182}
1183
1184fn nfa_exec(nfa: &Nfa, input: &[u8]) -> c_int {
1189 if nfa.states.is_empty() {
1190 return REGEXP_ERROR;
1191 }
1192
1193 let mut current = epsilon_closure(nfa, &[nfa.start]);
1195
1196 if input.is_empty() {
1199 let mut final_states = current.clone();
1200 for &s in ¤t {
1201 for (cond, target) in &nfa.states[s].transitions {
1202 if let Transition::Anchor(AnchorType::End) = cond {
1203 if *target < nfa.states.len() {
1204 let ec = epsilon_closure(nfa, &[*target]);
1205 final_states.extend(ec);
1206 }
1207 }
1208 }
1209 }
1210 final_states = epsilon_closure(nfa, &final_states);
1211 return if has_accept_state(nfa, &final_states) {
1212 REGEXP_MATCH
1213 } else {
1214 REGEXP_NOMATCH
1215 };
1216 }
1217
1218 for (i, &c) in input.iter().enumerate() {
1219 let is_start = i == 0;
1220 let next_states = move_on_char(nfa, ¤t, c, is_start, false);
1223 if next_states.is_empty() {
1224 return REGEXP_NOMATCH;
1225 }
1226
1227 current = epsilon_closure(nfa, &next_states);
1228 if current.is_empty() {
1229 return REGEXP_NOMATCH;
1230 }
1231 }
1232
1233 let mut final_states = current.clone();
1237 for &s in ¤t {
1238 for (cond, target) in &nfa.states[s].transitions {
1239 if let Transition::Anchor(AnchorType::End) = cond {
1240 if *target < nfa.states.len() {
1241 let ec = epsilon_closure(nfa, &[*target]);
1242 final_states.extend(ec);
1243 }
1244 }
1245 }
1246 }
1247 final_states = epsilon_closure(nfa, &final_states);
1248
1249 if has_accept_state(nfa, &final_states) {
1250 REGEXP_MATCH
1251 } else {
1252 REGEXP_NOMATCH
1253 }
1254}
1255
1256#[derive(Debug)]
1266#[repr(C)]
1267pub struct XmlRegexp {
1268 pattern: *mut xmlChar,
1270 nfa: Option<Box<Nfa>>,
1272 is_deterministic: c_int,
1274}
1275
1276fn compile_pattern(pattern: &[u8]) -> Option<Box<Nfa>> {
1278 if pattern.is_empty() {
1279 let mut nfa = Nfa::new();
1281 nfa.set_accept(nfa.start);
1282 return Some(Box::new(nfa));
1283 }
1284
1285 let mut parser = RegexParser::new(pattern);
1286 match parser.parse() {
1287 Ok(fragment) => {
1288 let mut nfa = fragment.nfa;
1294 for state in &mut nfa.states {
1295 state.is_accept = false;
1296 }
1297 for &s in &fragment.out {
1298 if s < nfa.states.len() {
1299 nfa.set_accept(s);
1300 }
1301 }
1302 Some(Box::new(nfa))
1303 }
1304 Err(_) => None,
1305 }
1306}
1307
1308fn is_deterministic(nfa: &Nfa) -> bool {
1310 for (state_idx, state) in nfa.states.iter().enumerate() {
1331 let epsilon_count = state
1333 .transitions
1334 .iter()
1335 .filter(|(cond, _)| matches!(cond, Transition::Epsilon))
1336 .count();
1337
1338 let consuming_count = state
1340 .transitions
1341 .iter()
1342 .filter(|(cond, _)| !matches!(cond, Transition::Epsilon | Transition::Anchor(_)))
1343 .count();
1344
1345 if epsilon_count > 1 {
1347 return false;
1348 }
1349
1350 if epsilon_count > 0 && consuming_count > 0 {
1352 return false;
1353 }
1354
1355 let closure = if epsilon_count == 0 {
1358 epsilon_closure(nfa, &[state_idx])
1359 } else {
1360 continue;
1362 };
1363
1364 let mut has_wildcard = false;
1365 let mut chars = Vec::new();
1366 let mut has_range = false;
1367 let mut has_not = false;
1368 let mut has_predefined = false;
1369
1370 for &s_idx in &closure {
1371 if s_idx >= nfa.states.len() {
1372 continue;
1373 }
1374 for (cond, _) in &nfa.states[s_idx].transitions {
1375 match cond {
1376 Transition::Epsilon | Transition::Anchor(_) => {}
1377 Transition::Wildcard => {
1378 if has_wildcard {
1379 return false;
1380 }
1381 has_wildcard = true;
1382 }
1383 Transition::Char(c) => {
1384 chars.push(*c);
1385 }
1386 Transition::Range(_, _) => {
1387 has_range = true;
1388 }
1389 Transition::Set(_) => {}
1390 Transition::NotRange(_, _) | Transition::NotSet(_) => {
1391 has_not = true;
1392 }
1393 Transition::Predefined(_) => {
1394 has_predefined = true;
1395 }
1396 }
1397 }
1398 }
1399
1400 if has_wildcard && (!chars.is_empty() || has_range || has_not || has_predefined) {
1402 return false;
1403 }
1404
1405 if has_range && (has_wildcard || has_not || has_predefined) {
1406 return false;
1407 }
1408
1409 if has_not && (has_wildcard || has_range || has_predefined) {
1410 return false;
1411 }
1412
1413 if has_predefined && (has_wildcard || has_range || has_not) {
1414 return false;
1415 }
1416
1417 chars.sort();
1419 let dedup_len = {
1420 chars.dedup();
1421 chars.len()
1422 };
1423 if dedup_len > 1 && (has_range || has_wildcard || has_not || has_predefined) {
1432 return false;
1433 }
1434 }
1435
1436 true
1437}
1438
1439const unsafe fn xml_strlen(s: *const xmlChar) -> usize {
1445 if s.is_null() {
1446 return 0;
1447 }
1448 let mut len: usize = 0;
1449 while *s.add(len) != 0 {
1450 len += 1;
1451 }
1452 len
1453}
1454
1455unsafe fn xml_strdup(s: *const xmlChar) -> *mut xmlChar {
1457 if s.is_null() {
1458 return ptr::null_mut();
1459 }
1460 let len = xml_strlen(s);
1461 let new_ptr = xmlMallocImpl((len + 1) * core::mem::size_of::<xmlChar>()) as *mut xmlChar;
1462 if new_ptr.is_null() {
1463 return ptr::null_mut();
1464 }
1465 for i in 0..=len {
1466 unsafe { *new_ptr.add(i) = *s.add(i) };
1467 }
1468 new_ptr
1469}
1470
1471#[no_mangle]
1485pub unsafe extern "C" fn xmlRegexpCompile(pattern: *const xmlChar) -> *mut XmlRegexp {
1486 if pattern.is_null() {
1487 return ptr::null_mut();
1488 }
1489
1490 let len = xml_strlen(pattern);
1491 let pattern_bytes = unsafe { core::slice::from_raw_parts(pattern, len) };
1492
1493 let nfa = compile_pattern(pattern_bytes);
1494 let pattern_copy = xml_strdup(pattern);
1495
1496 let compiled = xmlMallocImpl(core::mem::size_of::<XmlRegexp>()) as *mut XmlRegexp;
1497 if compiled.is_null() {
1498 if !pattern_copy.is_null() {
1499 xmlFreeImpl(pattern_copy as *mut c_void);
1500 }
1501 return ptr::null_mut();
1502 }
1503
1504 let det = nfa
1505 .as_ref()
1506 .map_or(0, |n| if is_deterministic(n) { 1 } else { 0 });
1507
1508 unsafe {
1509 (*compiled).pattern = pattern_copy;
1510 core::ptr::write(&mut (*compiled).nfa, nfa);
1514 (*compiled).is_deterministic = det;
1515 }
1516
1517 compiled
1518}
1519
1520#[no_mangle]
1535pub unsafe extern "C" fn xmlRegexpExec(compiled: *const XmlRegexp, value: *const xmlChar) -> c_int {
1536 if compiled.is_null() || value.is_null() {
1537 return REGEXP_ERROR;
1538 }
1539
1540 let regex = unsafe { &*compiled };
1541 let nfa = match ®ex.nfa {
1542 Some(nfa) => nfa,
1543 None => return REGEXP_ERROR,
1544 };
1545
1546 let len = xml_strlen(value);
1547 let input = unsafe { core::slice::from_raw_parts(value, len) };
1548
1549 nfa_exec(nfa, input)
1550}
1551
1552#[no_mangle]
1566pub const unsafe extern "C" fn xmlRegexpIsDeterministic(compiled: *const XmlRegexp) -> c_int {
1567 if compiled.is_null() {
1568 return 0;
1569 }
1570 unsafe { (*compiled).is_deterministic }
1571}
1572
1573#[no_mangle]
1585pub unsafe extern "C" fn xmlRegexpPrint(compiled: *const XmlRegexp) {
1586 if compiled.is_null() {
1587 eprintln!("(null regex)");
1588 return;
1589 }
1590
1591 let regex = unsafe { &*compiled };
1592 let pattern_str = if regex.pattern.is_null() {
1593 "(null)"
1594 } else {
1595 let len = xml_strlen(regex.pattern);
1596 let slice = unsafe { core::slice::from_raw_parts(regex.pattern, len) };
1597 core::str::from_utf8(slice).unwrap_or("(invalid utf-8)")
1598 };
1599
1600 eprintln!("Regex: /{}/", pattern_str);
1601 eprintln!(" deterministic: {}", regex.is_deterministic);
1602
1603 if let Some(ref nfa) = regex.nfa {
1604 eprintln!(" states: {}", nfa.states.len());
1605 eprintln!(" start state: {}", nfa.start);
1606 for (i, state) in nfa.states.iter().enumerate() {
1607 eprint!(" state[{}]: ", i);
1608 if state.is_accept {
1609 eprint!("(accept) ");
1610 }
1611 for (j, (cond, target)) in state.transitions.iter().enumerate() {
1612 if j > 0 {
1613 eprint!(", ");
1614 }
1615 match cond {
1616 Transition::Epsilon => eprint!("ε->{}", target),
1617 Transition::Char(c) => {
1618 if *c >= 0x20 && *c <= 0x7e {
1619 eprint!("'{}'->{}", *c as char, target);
1620 } else {
1621 eprint!("0x{:02x}->{}", c, target);
1622 }
1623 }
1624 Transition::Range(lo, hi) => {
1625 eprint!("[{:02x}-{:02x}]->{}", lo, hi, target);
1626 }
1627 Transition::Set(chars) => {
1628 eprint!("{{");
1629 for (k, c) in chars.iter().enumerate() {
1630 if k > 0 {
1631 eprint!(",");
1632 }
1633 eprint!("0x{:02x}", c);
1634 }
1635 eprint!("}}->{}", target);
1636 }
1637 Transition::NotRange(lo, hi) => {
1638 eprint!("[^{:02x}-{:02x}]->{}", lo, hi, target);
1639 }
1640 Transition::NotSet(chars) => {
1641 eprint!("^{{");
1642 for (k, c) in chars.iter().enumerate() {
1643 if k > 0 {
1644 eprint!(",");
1645 }
1646 eprint!("0x{:02x}", c);
1647 }
1648 eprint!("}}->{}", target);
1649 }
1650 Transition::Wildcard => eprint!(".*->{}", target),
1651 Transition::Predefined(class) => {
1652 let name = match class {
1653 PredefinedClass::Digit => "\\d",
1654 PredefinedClass::NotDigit => "\\D",
1655 PredefinedClass::Space => "\\s",
1656 PredefinedClass::NotSpace => "\\S",
1657 PredefinedClass::Word => "\\w",
1658 PredefinedClass::NotWord => "\\W",
1659 };
1660 eprint!("{}->{}", name, target);
1661 }
1662 Transition::Anchor(at) => match at {
1663 AnchorType::Start => eprint!("^->{}", target),
1664 AnchorType::End => eprint!("$->{}", target),
1665 },
1666 }
1667 }
1668 eprintln!();
1669 }
1670 }
1671}
1672
1673#[no_mangle]
1686pub unsafe extern "C" fn xmlRegFreeRegexp(regexp: *mut XmlRegexp) {
1687 if regexp.is_null() {
1688 return;
1689 }
1690 unsafe {
1691 if !(*regexp).pattern.is_null() {
1692 xmlFreeImpl((*regexp).pattern as *mut c_void);
1693 }
1694 let _ = (*regexp).nfa.take();
1696 xmlFreeImpl(regexp as *mut c_void);
1697 }
1698}
1699
1700#[derive(Debug)]
1710#[repr(C)]
1711pub struct RegExecCtxt {
1712 compiled: *mut XmlRegexp,
1714 current_states: Vec<usize>,
1716 started: bool,
1718}
1719
1720#[no_mangle]
1732pub unsafe extern "C" fn xmlRegNewExecCtxt(
1733 compiled: *mut XmlRegexp,
1734 _data: *mut c_void,
1735) -> *mut RegExecCtxt {
1736 if compiled.is_null() {
1737 return ptr::null_mut();
1738 }
1739
1740 let ctxt = xmlMallocImpl(core::mem::size_of::<RegExecCtxt>()) as *mut RegExecCtxt;
1741 if ctxt.is_null() {
1742 return ptr::null_mut();
1743 }
1744
1745 unsafe {
1746 (*ctxt).compiled = compiled;
1747 core::ptr::write(&mut (*ctxt).current_states, Vec::new());
1751 (*ctxt).started = false;
1752 }
1753
1754 ctxt
1755}
1756
1757#[no_mangle]
1773pub unsafe extern "C" fn xmlRegExecPushString(
1774 ctxt: *mut RegExecCtxt,
1775 value: *const xmlChar,
1776) -> c_int {
1777 if ctxt.is_null() {
1778 return REGEXP_ERROR;
1779 }
1780
1781 let exec_ctxt = unsafe { &mut *ctxt };
1782 let regex = match unsafe { exec_ctxt.compiled.as_mut() } {
1783 Some(r) => r,
1784 None => return REGEXP_ERROR,
1785 };
1786
1787 let nfa = match ®ex.nfa {
1788 Some(nfa) => nfa,
1789 None => return REGEXP_ERROR,
1790 };
1791
1792 if value.is_null() {
1793 if !exec_ctxt.started {
1796 exec_ctxt.current_states = epsilon_closure(nfa, &[nfa.start]);
1797 exec_ctxt.started = true;
1798 }
1799 return if has_accept_state(nfa, &exec_ctxt.current_states) {
1800 REGEXP_MATCH
1801 } else {
1802 REGEXP_NOMATCH
1803 };
1804 }
1805
1806 let len = xml_strlen(value);
1807 let input = unsafe { core::slice::from_raw_parts(value, len) };
1808
1809 if input.is_empty() {
1810 return REGEXP_NOMATCH;
1811 }
1812
1813 if !exec_ctxt.started {
1814 exec_ctxt.current_states = epsilon_closure(nfa, &[nfa.start]);
1816 exec_ctxt.started = true;
1817 }
1818
1819 for (i, &c) in input.iter().enumerate() {
1820 let is_end = i == input.len() - 1 && true; let next_states = move_on_char(nfa, &exec_ctxt.current_states, c, i == 0, is_end);
1822 if next_states.is_empty() {
1823 exec_ctxt.current_states = Vec::new();
1824 return REGEXP_NOMATCH;
1825 }
1826 exec_ctxt.current_states = epsilon_closure(nfa, &next_states);
1827 }
1828
1829 if has_accept_state(nfa, &exec_ctxt.current_states) {
1831 REGEXP_MATCH
1832 } else {
1833 REGEXP_NOMATCH
1834 }
1835}
1836
1837#[no_mangle]
1850pub unsafe extern "C" fn xmlRegFreeExecCtxt(ctxt: *mut RegExecCtxt) {
1851 if ctxt.is_null() {
1852 return;
1853 }
1854 unsafe {
1855 core::ptr::drop_in_place(&mut (*ctxt).current_states);
1858 xmlFreeImpl(ctxt as *mut c_void);
1859 }
1860}
1861
1862#[cfg(test)]
1867mod tests {
1868 use super::*;
1869 use core::ptr;
1870
1871 fn to_xml_str(s: &[u8]) -> *mut xmlChar {
1876 let len = s.len();
1877 let ptr =
1878 unsafe { xmlMallocImpl((len + 1) * core::mem::size_of::<xmlChar>()) } as *mut xmlChar;
1879 if ptr.is_null() {
1880 return ptr::null_mut();
1881 }
1882 unsafe {
1883 core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, len);
1884 *ptr.add(len) = 0;
1885 }
1886 ptr
1887 }
1888
1889 fn match_regex(pattern: &[u8], input: &[u8]) -> c_int {
1891 let pat = to_xml_str(pattern);
1892 let val = to_xml_str(input);
1893 unsafe {
1894 let compiled = xmlRegexpCompile(pat);
1895 if compiled.is_null() {
1896 return REGEXP_ERROR;
1897 }
1898 let ret = xmlRegexpExec(compiled, val);
1899 xmlRegFreeRegexp(compiled);
1900 ret
1901 }
1902 }
1903
1904 fn compile(pattern: &[u8]) -> *mut XmlRegexp {
1906 let pat = to_xml_str(pattern);
1907 unsafe { xmlRegexpCompile(pat) }
1908 }
1909
1910 #[test]
1913 fn test_literal_exact() {
1914 assert_eq!(match_regex(b"hello", b"hello"), REGEXP_MATCH);
1915 }
1916
1917 #[test]
1918 fn test_literal_no_match() {
1919 assert_eq!(match_regex(b"hello", b"world"), REGEXP_NOMATCH);
1920 }
1921
1922 #[test]
1923 fn test_literal_partial_prefix() {
1924 assert_eq!(match_regex(b"hello", b"hel"), REGEXP_NOMATCH);
1926 }
1927
1928 #[test]
1929 fn test_literal_empty_pattern() {
1930 assert_eq!(match_regex(b"", b""), REGEXP_MATCH);
1932 }
1933
1934 #[test]
1935 fn test_literal_empty_pattern_nonempty() {
1936 assert_eq!(match_regex(b"", b"a"), REGEXP_NOMATCH);
1938 }
1939
1940 #[test]
1941 fn test_literal_single_char() {
1942 assert_eq!(match_regex(b"a", b"a"), REGEXP_MATCH);
1943 assert_eq!(match_regex(b"a", b"b"), REGEXP_NOMATCH);
1944 }
1945
1946 #[test]
1949 fn test_alternation_simple() {
1950 assert_eq!(match_regex(b"a|b", b"a"), REGEXP_MATCH);
1951 assert_eq!(match_regex(b"a|b", b"b"), REGEXP_MATCH);
1952 assert_eq!(match_regex(b"a|b", b"c"), REGEXP_NOMATCH);
1953 }
1954
1955 #[test]
1956 fn test_alternation_three() {
1957 assert_eq!(match_regex(b"a|b|c", b"a"), REGEXP_MATCH);
1958 assert_eq!(match_regex(b"a|b|c", b"b"), REGEXP_MATCH);
1959 assert_eq!(match_regex(b"a|b|c", b"c"), REGEXP_MATCH);
1960 assert_eq!(match_regex(b"a|b|c", b"d"), REGEXP_NOMATCH);
1961 }
1962
1963 #[test]
1966 fn test_zero_or_more() {
1967 assert_eq!(match_regex(b"a*", b""), REGEXP_MATCH);
1968 assert_eq!(match_regex(b"a*", b"a"), REGEXP_MATCH);
1969 assert_eq!(match_regex(b"a*", b"aaa"), REGEXP_MATCH);
1970 }
1971
1972 #[test]
1973 fn test_one_or_more() {
1974 assert_eq!(match_regex(b"a+", b"a"), REGEXP_MATCH);
1975 assert_eq!(match_regex(b"a+", b"aaa"), REGEXP_MATCH);
1976 assert_eq!(match_regex(b"a+", b""), REGEXP_NOMATCH);
1977 }
1978
1979 #[test]
1980 fn test_zero_or_one() {
1981 assert_eq!(match_regex(b"a?", b""), REGEXP_MATCH);
1982 assert_eq!(match_regex(b"a?", b"a"), REGEXP_MATCH);
1983 assert_eq!(match_regex(b"a?", b"aa"), REGEXP_NOMATCH);
1984 }
1985
1986 #[test]
1987 fn test_zero_or_more_middle() {
1988 assert_eq!(match_regex(b"a*b", b"b"), REGEXP_MATCH);
1990 assert_eq!(match_regex(b"a*b", b"ab"), REGEXP_MATCH);
1991 assert_eq!(match_regex(b"a*b", b"aaab"), REGEXP_MATCH);
1992 assert_eq!(match_regex(b"a*b", b"a"), REGEXP_NOMATCH);
1993 }
1994
1995 #[test]
1998 fn test_grouping() {
1999 assert_eq!(match_regex(b"(a)", b"a"), REGEXP_MATCH);
2000 assert_eq!(match_regex(b"(a)", b"b"), REGEXP_NOMATCH);
2001 }
2002
2003 #[test]
2004 fn test_grouping_with_quantifier() {
2005 assert_eq!(match_regex(b"(ab)+", b"ab"), REGEXP_MATCH);
2006 assert_eq!(match_regex(b"(ab)+", b"abab"), REGEXP_MATCH);
2007 assert_eq!(match_regex(b"(ab)+", b"a"), REGEXP_NOMATCH);
2008 }
2009
2010 #[test]
2013 fn test_start_anchor() {
2014 assert_eq!(match_regex(b"^a", b"a"), REGEXP_MATCH);
2015 assert_eq!(match_regex(b"^a", b"ba"), REGEXP_NOMATCH);
2016 }
2017
2018 #[test]
2019 fn test_end_anchor() {
2020 assert_eq!(match_regex(b"a$", b"a"), REGEXP_MATCH);
2021 assert_eq!(match_regex(b"a$", b"ba"), REGEXP_NOMATCH);
2022 }
2023
2024 #[test]
2025 fn test_both_anchors() {
2026 assert_eq!(match_regex(b"^a$", b"a"), REGEXP_MATCH);
2027 assert_eq!(match_regex(b"^a$", b"ab"), REGEXP_NOMATCH);
2028 assert_eq!(match_regex(b"^a$", b"ba"), REGEXP_NOMATCH);
2029 }
2030
2031 #[test]
2034 fn test_wildcard() {
2035 assert_eq!(match_regex(b".", b"a"), REGEXP_MATCH);
2036 assert_eq!(match_regex(b".", b"1"), REGEXP_MATCH);
2037 assert_eq!(match_regex(b"...", b"abc"), REGEXP_MATCH);
2038 assert_eq!(match_regex(b"...", b"ab"), REGEXP_NOMATCH);
2039 }
2040
2041 #[test]
2042 fn test_wildcard_with_literal() {
2043 assert_eq!(match_regex(b"a.b", b"axb"), REGEXP_MATCH);
2044 assert_eq!(match_regex(b"a.b", b"azb"), REGEXP_MATCH);
2045 assert_eq!(match_regex(b"a.b", b"ab"), REGEXP_NOMATCH);
2046 }
2047
2048 #[test]
2051 fn test_escaped_newline() {
2052 assert_eq!(match_regex(b"a\\nb", b"a\nb"), REGEXP_MATCH);
2053 assert_eq!(match_regex(b"a\\nb", b"ab"), REGEXP_NOMATCH);
2054 }
2055
2056 #[test]
2057 fn test_escaped_tab() {
2058 assert_eq!(match_regex(b"a\\tb", b"a\tb"), REGEXP_MATCH);
2059 }
2060
2061 #[test]
2062 fn test_escaped_metachar() {
2063 assert_eq!(match_regex(b"\\.", b"."), REGEXP_MATCH);
2065 assert_eq!(match_regex(b"\\.", b"a"), REGEXP_NOMATCH);
2066 }
2067
2068 #[test]
2071 fn test_char_class_single() {
2072 assert_eq!(match_regex(b"[a]", b"a"), REGEXP_MATCH);
2073 assert_eq!(match_regex(b"[a]", b"b"), REGEXP_NOMATCH);
2074 }
2075
2076 #[test]
2077 fn test_char_class_multiple_chars() {
2078 assert_eq!(match_regex(b"[abc]", b"a"), REGEXP_MATCH);
2079 assert_eq!(match_regex(b"[abc]", b"b"), REGEXP_MATCH);
2080 assert_eq!(match_regex(b"[abc]", b"c"), REGEXP_MATCH);
2081 assert_eq!(match_regex(b"[abc]", b"d"), REGEXP_NOMATCH);
2082 }
2083
2084 #[test]
2085 fn test_char_class_range() {
2086 assert_eq!(match_regex(b"[a-z]", b"a"), REGEXP_MATCH);
2087 assert_eq!(match_regex(b"[a-z]", b"m"), REGEXP_MATCH);
2088 assert_eq!(match_regex(b"[a-z]", b"z"), REGEXP_MATCH);
2089 assert_eq!(match_regex(b"[a-z]", b"1"), REGEXP_NOMATCH);
2090 }
2091
2092 #[test]
2093 fn test_char_class_range_with_escape() {
2094 assert_eq!(match_regex(b"[\\d]", b"5"), REGEXP_MATCH);
2095 assert_eq!(match_regex(b"[\\d]", b"a"), REGEXP_NOMATCH);
2096 }
2097
2098 #[test]
2099 fn test_multiple_char_classes() {
2100 assert_eq!(match_regex(b"[a-z0-9]+", b"abc123"), REGEXP_MATCH);
2101 assert_eq!(match_regex(b"[a-z0-9]+", b"ABC"), REGEXP_NOMATCH);
2102 }
2103
2104 #[test]
2107 fn test_digit_class() {
2108 assert_eq!(match_regex(b"\\d", b"5"), REGEXP_MATCH);
2109 assert_eq!(match_regex(b"\\d", b"a"), REGEXP_NOMATCH);
2110 }
2111
2112 #[test]
2113 fn test_word_class() {
2114 assert_eq!(match_regex(b"\\w+", b"hello"), REGEXP_MATCH);
2115 assert_eq!(match_regex(b"\\w+", b"hello123"), REGEXP_MATCH);
2116 assert_eq!(match_regex(b"\\w+", b""), REGEXP_NOMATCH);
2117 }
2118
2119 #[test]
2120 fn test_space_class() {
2121 assert_eq!(match_regex(b"\\s", b" "), REGEXP_MATCH);
2122 assert_eq!(match_regex(b"\\s", b"\t"), REGEXP_MATCH);
2123 assert_eq!(match_regex(b"\\s", b"a"), REGEXP_NOMATCH);
2124 }
2125
2126 #[test]
2129 fn test_complex_email_like() {
2130 assert_eq!(
2132 match_regex(b"\\w+@\\w+\\.\\w+", b"user@example.com"),
2133 REGEXP_MATCH
2134 );
2135 assert_eq!(match_regex(b"\\w+@\\w+\\.\\w+", b"invalid"), REGEXP_NOMATCH);
2136 }
2137
2138 #[test]
2139 fn test_complex_phone_like() {
2140 assert_eq!(
2142 match_regex(b"\\d{3}-\\d{3}-\\d{4}", b"555-123-4567"),
2143 REGEXP_MATCH
2144 );
2145 assert_eq!(
2146 match_regex(b"\\d{3}-\\d{3}-\\d{4}", b"555-123-456"),
2147 REGEXP_NOMATCH
2148 );
2149 }
2150
2151 #[test]
2152 fn test_pattern_with_all_features() {
2153 assert_eq!(match_regex(b"^(a|b)+c$", b"ac"), REGEXP_MATCH);
2155 assert_eq!(match_regex(b"^(a|b)+c$", b"bc"), REGEXP_MATCH);
2156 assert_eq!(match_regex(b"^(a|b)+c$", b"ababc"), REGEXP_MATCH);
2157 assert_eq!(match_regex(b"^(a|b)+c$", b"abd"), REGEXP_NOMATCH);
2158 }
2159
2160 #[test]
2163 fn test_exact_quantifier() {
2164 assert_eq!(match_regex(b"a{3}", b"aaa"), REGEXP_MATCH);
2165 assert_eq!(match_regex(b"a{3}", b"aa"), REGEXP_NOMATCH);
2166 assert_eq!(match_regex(b"a{3}", b"aaaa"), REGEXP_NOMATCH);
2167 }
2168
2169 #[test]
2170 fn test_between_quantifier() {
2171 assert_eq!(match_regex(b"a{2,4}", b"aa"), REGEXP_MATCH);
2172 assert_eq!(match_regex(b"a{2,4}", b"aaa"), REGEXP_MATCH);
2173 assert_eq!(match_regex(b"a{2,4}", b"aaaa"), REGEXP_MATCH);
2174 assert_eq!(match_regex(b"a{2,4}", b"a"), REGEXP_NOMATCH);
2175 assert_eq!(match_regex(b"a{2,4}", b"aaaaa"), REGEXP_NOMATCH);
2176 }
2177
2178 #[test]
2179 fn test_at_least_quantifier() {
2180 assert_eq!(match_regex(b"a{2,}", b"aa"), REGEXP_MATCH);
2181 assert_eq!(match_regex(b"a{2,}", b"aaaa"), REGEXP_MATCH);
2182 assert_eq!(match_regex(b"a{2,}", b"a"), REGEXP_NOMATCH);
2183 }
2184
2185 #[test]
2188 fn test_deterministic_literal() {
2189 let compiled = compile(b"hello");
2190 assert!(!compiled.is_null());
2191 assert_eq!(unsafe { xmlRegexpIsDeterministic(compiled) }, 1);
2192 unsafe { xmlRegFreeRegexp(compiled) };
2193 }
2194
2195 #[test]
2196 fn test_non_deterministic() {
2197 let compiled = compile(b"a|b");
2199 assert!(!compiled.is_null());
2200 assert_eq!(unsafe { xmlRegexpIsDeterministic(compiled) }, 0);
2201 unsafe { xmlRegFreeRegexp(compiled) };
2202 }
2203
2204 #[test]
2207 fn test_incremental_empty_input() {
2208 let compiled = compile(b"a*");
2209 assert!(!compiled.is_null());
2210 let ctxt = unsafe { xmlRegNewExecCtxt(compiled, ptr::null_mut()) };
2211 assert!(!ctxt.is_null());
2212 let ret = unsafe { xmlRegExecPushString(ctxt, ptr::null_mut()) };
2214 assert_eq!(ret, REGEXP_MATCH);
2216 unsafe { xmlRegFreeExecCtxt(ctxt) };
2217 unsafe { xmlRegFreeRegexp(compiled) };
2218 }
2219
2220 #[test]
2221 fn test_incremental_simple_match() {
2222 let compiled = compile(b"abc");
2223 assert!(!compiled.is_null());
2224 let ctxt = unsafe { xmlRegNewExecCtxt(compiled, ptr::null_mut()) };
2225 assert!(!ctxt.is_null());
2226 let val = to_xml_str(b"abc");
2227 let ret = unsafe { xmlRegExecPushString(ctxt, val) };
2228 assert_eq!(ret, REGEXP_MATCH);
2229 unsafe { xmlRegFreeExecCtxt(ctxt) };
2230 unsafe { xmlRegFreeRegexp(compiled) };
2231 }
2232
2233 #[test]
2236 fn test_null_pattern() {
2237 let compiled = unsafe { xmlRegexpCompile(ptr::null()) };
2238 assert!(compiled.is_null());
2239 }
2240
2241 #[test]
2242 fn test_null_input() {
2243 let compiled = compile(b"a");
2244 assert!(!compiled.is_null());
2245 let ret = unsafe { xmlRegexpExec(compiled, ptr::null()) };
2246 assert_eq!(ret, REGEXP_ERROR);
2247 unsafe { xmlRegFreeRegexp(compiled) };
2248 }
2249
2250 #[test]
2251 fn test_double_free() {
2252 let compiled = compile(b"test");
2253 assert!(!compiled.is_null());
2254 unsafe { xmlRegFreeRegexp(compiled) };
2255 }
2258
2259 #[test]
2260 fn test_print() {
2261 let compiled = compile(b"hello");
2262 assert!(!compiled.is_null());
2263 unsafe { xmlRegexpPrint(compiled) };
2264 unsafe { xmlRegFreeRegexp(compiled) };
2265 }
2266}