Skip to main content

tre_regex_sys/
lib.rs

1// SPDX-License-Identifier: BSD-2-Clause
2// See LICENSE file in the project root for full license text.
3
4#![allow(non_upper_case_globals)]
5#![allow(non_camel_case_types)]
6#![allow(non_snake_case)]
7#![deny(unsafe_op_in_unsafe_fn)]
8
9include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
10
11#[cfg(test)]
12mod tests {
13    use super::*;
14    use std::ffi::c_int;
15    use std::mem;
16
17    #[cfg(feature = "approx")]
18    #[test]
19    fn reganexec() {
20        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
21        if unsafe {
22            tre_regcomp(
23                preg.as_mut_ptr(),
24                b"Hello!\0".as_ptr() as *const _,
25                REG_ICASE as c_int,
26            )
27        } != 0
28        {
29            panic!("tre_regcomp");
30        }
31        let preg = unsafe { preg.assume_init() };
32
33        let params = regaparams_t {
34            cost_ins: 1,
35            cost_del: 1,
36            cost_subst: 1,
37            max_cost: 2,
38            max_del: 2,
39            max_ins: 2,
40            max_subst: 2,
41            max_err: 2,
42        };
43
44        let mut pmatch: Vec<regmatch_t> = vec![Default::default(); 1];
45        let mut amatch = regamatch_t {
46            nmatch: 1,
47            pmatch: pmatch.as_mut_ptr(),
48            ..Default::default()
49        };
50
51        if unsafe {
52            tre_reganexec(
53                &preg,
54                b"Hullo!".as_ptr() as *const _,
55                6,
56                &mut amatch,
57                params,
58                0,
59            )
60        } != 0
61        {
62            panic!("tre_regaexec");
63        }
64
65        assert_eq!(amatch.cost, 1);
66        assert_eq!(pmatch[0].rm_so, 0);
67        assert_eq!(pmatch[0].rm_eo, 6);
68    }
69
70    #[test]
71    fn regexec() {
72        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
73        if unsafe {
74            tre_regcomp(
75                preg.as_mut_ptr(),
76                b"Hello(, [[:alpha:]]+)?!\0".as_ptr() as *const _,
77                (REG_EXTENDED | REG_ICASE) as c_int,
78            )
79        } != 0
80        {
81            panic!("tre_regcomp");
82        }
83
84        let preg = unsafe { preg.assume_init() };
85
86        let nmatch = 1;
87        let mut pmatch: Vec<regmatch_t> = vec![regmatch_t { rm_so: 0, rm_eo: 0 }; 1];
88        if unsafe {
89            tre_regexec(
90                &preg,
91                b"Hello!".as_ptr() as *const _,
92                nmatch,
93                pmatch.as_mut_ptr(),
94                0,
95            )
96        } != 0
97        {
98            panic!("tre_regexec");
99        }
100
101        assert!(pmatch[0].rm_so == 0, "Bad starting offset");
102        assert!(pmatch[0].rm_eo == 6, "Bad ending offset");
103
104        pmatch[0].rm_eo = 0;
105
106        let nmatch = 2;
107        pmatch.push(regmatch_t { rm_so: 0, rm_eo: 0 });
108        if unsafe {
109            tre_regexec(
110                &preg,
111                b"Hello, world!\0".as_ptr() as *const _,
112                nmatch,
113                pmatch.as_mut_ptr(),
114                0,
115            )
116        } != 0
117        {
118            panic!("tre_regexec");
119        }
120
121        assert!(pmatch[0].rm_so == 0, "Bad starting offset");
122        assert!(pmatch[0].rm_eo == 13, "Bad ending offset");
123        assert!(pmatch[1].rm_so == 5, "Bad starting offset for match group");
124        assert!(pmatch[1].rm_eo == 12, "Bad ending offset for match group");
125    }
126
127    #[cfg(feature = "vendored")]
128    #[test]
129    fn regnexecb_with_embedded_nul() {
130        let pattern = b"a\0b";
131        let string = b"xxa\0byy";
132        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
133
134        assert_eq!(
135            unsafe {
136                tre_regncompb(
137                    preg.as_mut_ptr(),
138                    pattern.as_ptr().cast(),
139                    pattern.len(),
140                    REG_EXTENDED as c_int,
141                )
142            },
143            0
144        );
145        let mut preg = unsafe { preg.assume_init() };
146        let mut pmatch = regmatch_t::default();
147
148        assert_eq!(
149            unsafe {
150                tre_regnexecb(
151                    &preg,
152                    string.as_ptr().cast(),
153                    string.len(),
154                    1,
155                    &mut pmatch,
156                    0,
157                )
158            },
159            0
160        );
161        assert_eq!(pmatch.rm_so, 2);
162        assert_eq!(pmatch.rm_eo, 5);
163
164        unsafe { tre_regfree(&mut preg) };
165    }
166
167    #[cfg(feature = "vendored")]
168    #[test]
169    fn regexecb_with_non_utf8_bytes() {
170        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
171        assert_eq!(
172            unsafe {
173                tre_regcompb(
174                    preg.as_mut_ptr(),
175                    b"\xff\0".as_ptr().cast(),
176                    REG_EXTENDED as c_int,
177                )
178            },
179            0
180        );
181        let mut preg = unsafe { preg.assume_init() };
182        let mut pmatch = regmatch_t::default();
183
184        assert_eq!(
185            unsafe { tre_regexecb(&preg, b"x\xff\0".as_ptr().cast(), 1, &mut pmatch, 0,) },
186            0
187        );
188        assert_eq!(pmatch.rm_so, 1);
189        assert_eq!(pmatch.rm_eo, 2);
190
191        unsafe { tre_regfree(&mut preg) };
192    }
193
194    #[cfg(all(feature = "vendored", feature = "approx"))]
195    #[test]
196    fn regaexecb_with_non_utf8_bytes() {
197        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
198        assert_eq!(
199            unsafe {
200                tre_regcompb(
201                    preg.as_mut_ptr(),
202                    b"\xff\0".as_ptr().cast(),
203                    REG_EXTENDED as c_int,
204                )
205            },
206            0
207        );
208        let mut preg = unsafe { preg.assume_init() };
209        let params = regaparams_t {
210            cost_ins: 1,
211            cost_del: 1,
212            cost_subst: 1,
213            max_cost: 1,
214            max_del: 1,
215            max_ins: 1,
216            max_subst: 1,
217            max_err: 1,
218        };
219        let mut pmatch = regmatch_t::default();
220        let mut amatch = regamatch_t {
221            nmatch: 1,
222            pmatch: &mut pmatch,
223            ..Default::default()
224        };
225
226        assert_eq!(
227            unsafe { tre_regaexecb(&preg, b"\xfe\0".as_ptr().cast(), &mut amatch, params, 0,) },
228            0
229        );
230        assert_eq!(amatch.cost, 1);
231        assert_eq!(pmatch.rm_so, 0);
232        assert_eq!(pmatch.rm_eo, 1);
233
234        unsafe { tre_regfree(&mut preg) };
235    }
236
237    #[test]
238    fn reguexec() {
239        use std::ffi::{c_int, c_uint, c_void};
240        #[repr(C)]
241        struct Data<'a>(pub &'a [u8], pub usize);
242
243        #[inline(never)]
244        #[unsafe(no_mangle)]
245        unsafe extern "C" fn get_next_char(
246            c: *mut tre_char_t,
247            pos_add: *mut c_uint,
248            context: *mut c_void,
249        ) -> c_int {
250            let data = context as *mut Data;
251            let (string, i) = unsafe { ((*data).0, (*data).1) };
252
253            if i >= string.len() {
254                unsafe { *c = b'\0' as tre_char_t };
255                return -1;
256            }
257
258            unsafe {
259                *c = string[i] as tre_char_t;
260                *pos_add = 1;
261                (*data).1 += 1;
262            }
263            0
264        }
265
266        #[inline(never)]
267        #[unsafe(no_mangle)]
268        unsafe extern "C" fn rewind(pos: usize, context: *mut c_void) {
269            let data = context as *mut Data;
270            unsafe { (*data).1 = pos };
271        }
272
273        #[inline(never)]
274        #[unsafe(no_mangle)]
275        unsafe extern "C" fn compare(
276            pos1: usize,
277            pos2: usize,
278            len: usize,
279            context: *mut c_void,
280        ) -> c_int {
281            let data = context as *mut Data;
282            let string = unsafe { (*data).0 };
283            let slen = string.len();
284
285            if pos1 > slen || pos2 > slen {
286                return -1;
287            }
288
289            let mut i1_s = pos1;
290            let mut i1_e = if i1_s + len > string.len() {
291                slen - 1
292            } else {
293                i1_s + len
294            };
295
296            let mut i2_s = pos2;
297            let mut i2_e = if i2_s + len > string.len() {
298                slen - 1
299            } else {
300                i2_s + len
301            };
302
303            if (i1_s > i1_e || i2_s > i2_e) || ((i1_e - i1_s) != (i2_e - i2_s)) {
304                // Different lengths, therefore unequal
305                return -1;
306            }
307
308            if i1_s > i2_s {
309                // Swap
310                std::mem::swap(&mut i1_s, &mut i2_s);
311                std::mem::swap(&mut i1_e, &mut i2_e);
312            }
313
314            if string[i1_s..i1_e] == string[i2_s..i2_e] {
315                return 0;
316            }
317
318            -1
319        }
320
321        let mut preg = mem::MaybeUninit::<regex_t>::uninit();
322        if unsafe {
323            tre_regcomp(
324                preg.as_mut_ptr(),
325                b"(abracadabra)(\\1)*\0".as_ptr() as *const _,
326                (REG_ICASE | REG_EXTENDED) as c_int,
327            )
328        } != 0
329        {
330            panic!("tre_regcomp");
331        }
332        let preg = unsafe { preg.assume_init() };
333
334        let string = b"abracadabraabracadabra";
335        let mut data = Data(string, 0);
336        let source = tre_str_source {
337            get_next_char: Some(get_next_char),
338            rewind: Some(rewind),
339            compare: Some(compare),
340            context: &mut data as *mut _ as *mut c_void,
341        };
342
343        let mut matches = vec![regmatch_t::default(); 1];
344        if unsafe { tre_reguexec(&preg, &source, 1, matches.as_mut_ptr(), 0) } != 0 {
345            panic!("tre_reguexec");
346        }
347
348        assert_eq!(matches[0].rm_so, 0);
349        assert_eq!(matches[0].rm_eo, 22);
350    }
351}