pcre2-0.2.9/.cargo_vcs_info.json0000644000000001360000000000100120570ustar { "git": { "sha1": "9687dca97a6c0f02ead8d0462fd3854e6f40abbe" }, "path_in_vcs": "" }pcre2-0.2.9/.github/workflows/ci.yml000064400000000000000000000032461046102023000153670ustar 00000000000000name: ci on: pull_request: push: branches: - master schedule: - cron: '00 01 * * *' jobs: test: name: test runs-on: ${{ matrix.os }} strategy: matrix: build: - pinned - stable - beta - nightly - macos - win-msvc - win-gnu include: - build: pinned os: ubuntu-latest rust: 1.70.0 - build: stable os: ubuntu-latest rust: stable - build: beta os: ubuntu-latest rust: beta - build: nightly os: ubuntu-latest rust: nightly - build: macos os: macos-latest rust: stable - build: win-msvc os: windows-latest rust: stable - build: win-gnu os: windows-latest rust: stable-x86_64-gnu steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Build run: cargo build --verbose --all - name: Build docs run: cargo doc --verbose --all - name: Run tests run: cargo test --verbose --all - name: Run tests with static build shell: bash run: PCRE2_SYS_STATIC=1 cargo test --verbose --all rustfmt: name: rustfmt runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable components: rustfmt - name: Check formatting run: | cargo fmt --all -- --check pcre2-0.2.9/.gitignore000064400000000000000000000000651046102023000126400ustar 00000000000000/target /pcre2-sys/target **/*.rs.bk Cargo.lock tags pcre2-0.2.9/.gitmodules000064400000000000000000000000001046102023000130120ustar 00000000000000pcre2-0.2.9/COPYING000064400000000000000000000001761046102023000117060ustar 00000000000000This project is dual-licensed under the Unlicense and MIT licenses. You may use this code under the terms of either license. pcre2-0.2.9/Cargo.toml0000644000000022530000000000100100570ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "pcre2" version = "0.2.9" authors = ["Andrew Gallant "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "High level wrapper library for PCRE2." homepage = "https://github.com/BurntSushi/rust-pcre2" documentation = "https://docs.rs/pcre2" readme = "README.md" keywords = [ "pcre", "pcre2", "regex", "jit", "perl", ] categories = ["text-processing"] license = "Unlicense OR MIT" repository = "https://github.com/BurntSushi/rust-pcre2" [lib] name = "pcre2" path = "src/lib.rs" [dependencies.libc] version = "0.2.146" [dependencies.log] version = "0.4.19" [dependencies.pcre2-sys] version = "0.2.9" pcre2-0.2.9/Cargo.toml.orig000064400000000000000000000011271046102023000135370ustar 00000000000000[package] name = "pcre2" version = "0.2.9" #:version authors = ["Andrew Gallant "] description = "High level wrapper library for PCRE2." documentation = "https://docs.rs/pcre2" homepage = "https://github.com/BurntSushi/rust-pcre2" repository = "https://github.com/BurntSushi/rust-pcre2" readme = "README.md" keywords = ["pcre", "pcre2", "regex", "jit", "perl"] license = "Unlicense OR MIT" categories = ["text-processing"] edition = "2021" [workspace] members = ["pcre2-sys"] [dependencies] libc = "0.2.146" log = "0.4.19" pcre2-sys = { version = "0.2.9", path = "pcre2-sys" } pcre2-0.2.9/LICENSE-MIT000064400000000000000000000020711046102023000123030ustar 00000000000000The MIT License (MIT) Copyright (c) 2017 Andrew Gallant Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pcre2-0.2.9/README.md000064400000000000000000000015721046102023000121330ustar 00000000000000pcre2 ===== A high level Rust wrapper library for [PCRE2](https://www.pcre.org/). [![Build status](https://github.com/BurntSushi/rust-pcre2/workflows/ci/badge.svg)](https://github.com/BurntSushi/rust-pcre2/actions) [![crates.io](https://img.shields.io/crates/v/pcre2.svg)](https://crates.io/crates/pcre2) Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org/). ### Documentation https://docs.rs/pcre2 ### Usage Run `cargo add pcre2` to add this crate to your `Cargo.toml` file. ### Notes Currently, this is a fairly light layer around PCRE2 itself and does not even come close to covering all of its functionality. There are no specific plans in place to build out the wrapper further, but PRs for making more of PCRE2 available are welcome, although my bandwidth for maintenance is limited. If you're interested in sharing this maintenance burden, please reach out. pcre2-0.2.9/UNLICENSE000064400000000000000000000022731046102023000121230ustar 00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to pcre2-0.2.9/rustfmt.toml000064400000000000000000000000541046102023000132470ustar 00000000000000max_width = 79 use_small_heuristics = "max" pcre2-0.2.9/src/bytes.rs000064400000000000000000001324141046102023000131370ustar 00000000000000use std::{ collections::HashMap, panic::{RefUnwindSafe, UnwindSafe}, sync::Arc, }; use pcre2_sys::{ PCRE2_CASELESS, PCRE2_DOTALL, PCRE2_EXTENDED, PCRE2_MATCH_INVALID_UTF, PCRE2_MULTILINE, PCRE2_NEWLINE_ANYCRLF, PCRE2_UCP, PCRE2_UNSET, PCRE2_UTF, }; use crate::{ error::Error, ffi::{Code, CompileContext, MatchConfig, MatchData}, pool::{Pool, PoolGuard}, }; /// Match represents a single match of a regex in a subject string. /// /// The lifetime parameter `'s` refers to the lifetime of the matched portion /// of the subject string. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Match<'s> { subject: &'s [u8], start: usize, end: usize, } impl<'s> Match<'s> { /// Returns the starting byte offset of the match in the subject. #[inline] pub fn start(&self) -> usize { self.start } /// Returns the ending byte offset of the match in the subject. #[inline] pub fn end(&self) -> usize { self.end } /// Returns the matched portion of the subject string. #[inline] pub fn as_bytes(&self) -> &'s [u8] { &self.subject[self.start..self.end] } /// Creates a new match from the given subject string and byte offsets. fn new(subject: &'s [u8], start: usize, end: usize) -> Match<'s> { Match { subject, start, end } } #[cfg(test)] fn as_pair(&self) -> (usize, usize) { (self.start, self.end) } } #[derive(Clone, Debug)] struct Config { /// PCRE2_CASELESS caseless: bool, /// PCRE2_DOTALL dotall: bool, /// PCRE2_EXTENDED extended: bool, /// PCRE2_MULTILINE multi_line: bool, /// PCRE2_NEWLINE_ANYCRLF crlf: bool, /// PCRE2_UCP ucp: bool, /// PCRE2_UTF utf: bool, /// use pcre2_jit_compile jit: JITChoice, /// Match-time specific configuration knobs. match_config: MatchConfig, } #[derive(Clone, Debug)] enum JITChoice { /// Never do JIT compilation. Never, /// Always do JIT compilation and return an error if it fails. Always, /// Attempt to do JIT compilation but silently fall back to non-JIT. Attempt, } impl Default for Config { fn default() -> Config { Config { caseless: false, dotall: false, extended: false, multi_line: false, crlf: false, ucp: false, utf: false, jit: JITChoice::Never, match_config: MatchConfig::default(), } } } /// A builder for configuring the compilation of a PCRE2 regex. #[derive(Clone, Debug)] pub struct RegexBuilder { config: Config, } impl RegexBuilder { /// Create a new builder with a default configuration. pub fn new() -> RegexBuilder { RegexBuilder { config: Config::default() } } /// Compile the given pattern into a PCRE regex using the current /// configuration. /// /// If there was a problem compiling the pattern, then an error is /// returned. pub fn build(&self, pattern: &str) -> Result { let mut options = 0; if self.config.caseless { options |= PCRE2_CASELESS; } if self.config.dotall { options |= PCRE2_DOTALL; } if self.config.extended { options |= PCRE2_EXTENDED; } if self.config.multi_line { options |= PCRE2_MULTILINE; } if self.config.ucp { options |= PCRE2_UCP; options |= PCRE2_UTF; options |= PCRE2_MATCH_INVALID_UTF; } if self.config.utf { options |= PCRE2_UTF; } let mut ctx = CompileContext::new(); if self.config.crlf { ctx.set_newline(PCRE2_NEWLINE_ANYCRLF) .expect("PCRE2_NEWLINE_ANYCRLF is a legal value"); } let mut code = Code::new(pattern, options, ctx)?; match self.config.jit { JITChoice::Never => {} // fallthrough JITChoice::Always => { code.jit_compile()?; } JITChoice::Attempt => { if let Err(err) = code.jit_compile() { log::debug!("JIT compilation failed: {}", err); } } } let capture_names = code.capture_names()?; let mut idx = HashMap::new(); for (i, group) in capture_names.iter().enumerate() { if let Some(ref name) = *group { idx.insert(name.to_string(), i); } } let code = Arc::new(code); let match_data = { let config = self.config.match_config.clone(); let code = Arc::clone(&code); let create: MatchDataPoolFn = Box::new(move || MatchData::new(config.clone(), &code)); Pool::new(create) }; Ok(Regex { config: Arc::new(self.config.clone()), pattern: pattern.to_string(), code, capture_names: Arc::new(capture_names), capture_names_idx: Arc::new(idx), match_data, }) } /// Enables case insensitive matching. /// /// If the `utf` option is also set, then Unicode case folding is used /// to determine case insensitivity. When the `utf` option is not set, /// then only standard ASCII case insensitivity is considered. /// /// This option corresponds to the `i` flag. pub fn caseless(&mut self, yes: bool) -> &mut RegexBuilder { self.config.caseless = yes; self } /// Enables "dot all" matching. /// /// When enabled, the `.` metacharacter in the pattern matches any /// character, include `\n`. When disabled (the default), `.` will match /// any character except for `\n`. /// /// This option corresponds to the `s` flag. pub fn dotall(&mut self, yes: bool) -> &mut RegexBuilder { self.config.dotall = yes; self } /// Enable "extended" mode in the pattern, where whitespace is ignored. /// /// This option corresponds to the `x` flag. pub fn extended(&mut self, yes: bool) -> &mut RegexBuilder { self.config.extended = yes; self } /// Enable multiline matching mode. /// /// When enabled, the `^` and `$` anchors will match both at the beginning /// and end of a subject string, in addition to matching at the start of /// a line and the end of a line. When disabled, the `^` and `$` anchors /// will only match at the beginning and end of a subject string. /// /// This option corresponds to the `m` flag. pub fn multi_line(&mut self, yes: bool) -> &mut RegexBuilder { self.config.multi_line = yes; self } /// Enable matching of CRLF as a line terminator. /// /// When enabled, anchors such as `^` and `$` will match any of the /// following as a line terminator: `\r`, `\n` or `\r\n`. /// /// This is disabled by default, in which case, only `\n` is recognized as /// a line terminator. pub fn crlf(&mut self, yes: bool) -> &mut RegexBuilder { self.config.crlf = yes; self } /// Enable Unicode matching mode. /// /// When enabled, the following patterns become Unicode aware: `\b`, `\B`, /// `\d`, `\D`, `\s`, `\S`, `\w`, `\W`. /// /// When set, this implies UTF matching mode. It is not possible to enable /// Unicode matching mode without enabling UTF matching mode. /// /// This is disabled by default. pub fn ucp(&mut self, yes: bool) -> &mut RegexBuilder { self.config.ucp = yes; self } /// Enable UTF matching mode. /// /// When enabled, characters are treated as sequences of code units that /// make up a single codepoint instead of as single bytes. For example, /// this will cause `.` to match any single UTF-8 encoded codepoint, where /// as when this is disabled, `.` will any single byte (except for `\n` in /// both cases, unless "dot all" mode is enabled). /// /// This is disabled by default. pub fn utf(&mut self, yes: bool) -> &mut RegexBuilder { self.config.utf = yes; self } /// This is now deprecated and is a no-op. /// /// Previously, this option permitted disabling PCRE2's UTF-8 validity /// check, which could result in undefined behavior if the haystack was /// not valid UTF-8. But PCRE2 introduced a new option, `PCRE2_MATCH_INVALID_UTF`, /// in 10.34 which this crate always sets. When this option is enabled, /// PCRE2 claims to not have undefined behavior when the haystack is /// invalid UTF-8. /// /// Therefore, disabling the UTF-8 check is not something that is exposed /// by this crate. #[deprecated( since = "0.2.4", note = "now a no-op due to new PCRE2 features" )] pub fn disable_utf_check(&mut self) -> &mut RegexBuilder { self } /// Enable PCRE2's JIT and return an error if it's not available. /// /// This generally speeds up matching quite a bit. The downside is that it /// can increase the time it takes to compile a pattern. /// /// If the JIT isn't available or if JIT compilation returns an error, then /// regex compilation will fail with the corresponding error. /// /// This is disabled by default, and always overrides `jit_if_available`. pub fn jit(&mut self, yes: bool) -> &mut RegexBuilder { if yes { self.config.jit = JITChoice::Always; } else { self.config.jit = JITChoice::Never; } self } /// Enable PCRE2's JIT if it's available. /// /// This generally speeds up matching quite a bit. The downside is that it /// can increase the time it takes to compile a pattern. /// /// If the JIT isn't available or if JIT compilation returns an error, /// then a debug message with the error will be emitted and the regex will /// otherwise silently fall back to non-JIT matching. /// /// This is disabled by default, and always overrides `jit`. pub fn jit_if_available(&mut self, yes: bool) -> &mut RegexBuilder { if yes { self.config.jit = JITChoice::Attempt; } else { self.config.jit = JITChoice::Never; } self } /// Set the maximum size of PCRE2's JIT stack, in bytes. If the JIT is /// not enabled, then this has no effect. /// /// When `None` is given, no custom JIT stack will be created, and instead, /// the default JIT stack is used. When the default is used, its maximum /// size is 32 KB. /// /// When this is set, then a new JIT stack will be created with the given /// maximum size as its limit. /// /// Increasing the stack size can be useful for larger regular expressions. /// /// By default, this is set to `None`. pub fn max_jit_stack_size( &mut self, bytes: Option, ) -> &mut RegexBuilder { self.config.match_config.max_jit_stack_size = bytes; self } } /// A compiled PCRE2 regular expression. /// /// This regex is safe to use from multiple threads simultaneously. For top /// performance, it is better to clone a new regex for each thread. pub struct Regex { /// The configuration used to build the regex. config: Arc, /// The original pattern string. pattern: String, /// The underlying compiled PCRE2 object. code: Arc, /// The capture group names for this regex. capture_names: Arc>>, /// A map from capture group name to capture group index. capture_names_idx: Arc>, /// A pool of mutable scratch data used by PCRE2 during matching. match_data: MatchDataPool, } impl Clone for Regex { fn clone(&self) -> Regex { let match_data = { let config = self.config.match_config.clone(); let code = Arc::clone(&self.code); let create: MatchDataPoolFn = Box::new(move || MatchData::new(config.clone(), &code)); Pool::new(create) }; Regex { config: Arc::clone(&self.config), pattern: self.pattern.clone(), code: Arc::clone(&self.code), capture_names: Arc::clone(&self.capture_names), capture_names_idx: Arc::clone(&self.capture_names_idx), match_data, } } } impl std::fmt::Debug for Regex { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Regex({:?})", self.pattern) } } impl Regex { /// Compiles a regular expression using the default configuration. /// /// Once compiled, it can be used repeatedly to search, split or replace /// text in a string. /// /// If an invalid expression is given, then an error is returned. /// /// To configure compilation options for the regex, use the /// [`RegexBuilder`](struct.RegexBuilder.html). pub fn new(pattern: &str) -> Result { RegexBuilder::new().build(pattern) } /// Returns true if and only if the regex matches the subject string given. /// /// # Example /// /// Test if some text contains at least one word with exactly 13 ASCII word /// bytes: /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let text = b"I categorically deny having triskaidekaphobia."; /// assert!(Regex::new(r"\b\w{13}\b")?.is_match(text)?); /// # Ok(()) }; example().unwrap() /// ``` pub fn is_match(&self, subject: &[u8]) -> Result { self.is_match_at(subject, 0) } /// Returns the start and end byte range of the leftmost-first match in /// `subject`. If no match exists, then `None` is returned. /// /// # Example /// /// Find the start and end location of the first word with exactly 13 /// ASCII word bytes: /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let text = b"I categorically deny having triskaidekaphobia."; /// let mat = Regex::new(r"\b\w{13}\b")?.find(text)?.unwrap(); /// assert_eq!((mat.start(), mat.end()), (2, 15)); /// # Ok(()) }; example().unwrap() /// ``` pub fn find<'s>( &self, subject: &'s [u8], ) -> Result>, Error> { self.find_at(subject, 0) } /// Returns an iterator for each successive non-overlapping match in /// `subject`, returning the start and end byte indices with respect to /// `subject`. /// /// # Example /// /// Find the start and end location of every word with exactly 13 ASCII /// word bytes: /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let text = b"Retroactively relinquishing remunerations is reprehensible."; /// for result in Regex::new(r"\b\w{13}\b")?.find_iter(text) { /// let mat = result?; /// println!("{:?}", mat); /// } /// # Ok(()) }; example().unwrap() /// ``` pub fn find_iter<'r, 's>(&'r self, subject: &'s [u8]) -> Matches<'r, 's> { Matches { re: self, match_data: self.match_data(), subject, last_end: 0, last_match: None, } } /// Returns the capture groups corresponding to the leftmost-first /// match in `subject`. Capture group `0` always corresponds to the entire /// match. If no match is found, then `None` is returned. /// /// # Examples /// /// Say you have some text with movie names and their release years, /// like "'Citizen Kane' (1941)". It'd be nice if we could search for text /// looking like that, while also extracting the movie name and its release /// year separately. /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let re = Regex::new(r"'([^']+)'\s+\((\d{4})\)")?; /// let text = b"Not my favorite movie: 'Citizen Kane' (1941)."; /// let caps = re.captures(text)?.unwrap(); /// assert_eq!(&caps[1], &b"Citizen Kane"[..]); /// assert_eq!(&caps[2], &b"1941"[..]); /// assert_eq!(&caps[0], &b"'Citizen Kane' (1941)"[..]); /// // You can also access the groups by index using the Index notation. /// // Note that this will panic on an invalid index. /// assert_eq!(&caps[1], b"Citizen Kane"); /// assert_eq!(&caps[2], b"1941"); /// assert_eq!(&caps[0], b"'Citizen Kane' (1941)"); /// # Ok(()) }; example().unwrap() /// ``` /// /// Note that the full match is at capture group `0`. Each subsequent /// capture group is indexed by the order of its opening `(`. /// /// We can make this example a bit clearer by using *named* capture groups: /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let re = Regex::new(r"'(?P[^']+)'\s+\((?P<year>\d{4})\)")?; /// let text = b"Not my favorite movie: 'Citizen Kane' (1941)."; /// let caps = re.captures(text)?.unwrap(); /// assert_eq!(&caps["title"], &b"Citizen Kane"[..]); /// assert_eq!(&caps["year"], &b"1941"[..]); /// assert_eq!(&caps[0], &b"'Citizen Kane' (1941)"[..]); /// // You can also access the groups by name using the Index notation. /// // Note that this will panic on an invalid group name. /// assert_eq!(&caps["title"], b"Citizen Kane"); /// assert_eq!(&caps["year"], b"1941"); /// assert_eq!(&caps[0], b"'Citizen Kane' (1941)"); /// # Ok(()) }; example().unwrap() /// ``` /// /// Here we name the capture groups, which we can access with the `name` /// method or the `Index` notation with a `&str`. Note that the named /// capture groups are still accessible with `get` or the `Index` notation /// with a `usize`. /// /// The `0`th capture group is always unnamed, so it must always be /// accessed with `get(0)` or `[0]`. pub fn captures<'s>( &self, subject: &'s [u8], ) -> Result<Option<Captures<'s>>, Error> { let mut locs = self.capture_locations(); Ok(self.captures_read(&mut locs, subject)?.map(move |_| Captures { subject, locs, idx: Arc::clone(&self.capture_names_idx), })) } /// Returns an iterator over all the non-overlapping capture groups matched /// in `subject`. This is operationally the same as `find_iter`, except it /// yields information about capturing group matches. /// /// # Example /// /// We can use this to find all movie titles and their release years in /// some text, where the movie is formatted like "'Title' (xxxx)": /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use std::str; /// /// use pcre2::bytes::Regex; /// /// let re = Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")?; /// let text = b"'Citizen Kane' (1941), 'The Wizard of Oz' (1939), 'M' (1931)."; /// for result in re.captures_iter(text) { /// let caps = result?; /// let title = str::from_utf8(&caps["title"]).unwrap(); /// let year = str::from_utf8(&caps["year"]).unwrap(); /// println!("Movie: {:?}, Released: {:?}", title, year); /// } /// // Output: /// // Movie: Citizen Kane, Released: 1941 /// // Movie: The Wizard of Oz, Released: 1939 /// // Movie: M, Released: 1931 /// # Ok(()) }; example().unwrap() /// ``` pub fn captures_iter<'r, 's>( &'r self, subject: &'s [u8], ) -> CaptureMatches<'r, 's> { CaptureMatches { re: self, subject, last_end: 0, last_match: None } } } /// Advanced or "lower level" search methods. impl Regex { /// Returns the same as is_match, but starts the search at the given /// offset. /// /// The significance of the starting point is that it takes the surrounding /// context into consideration. For example, the `\A` anchor can only /// match when `start == 0`. pub fn is_match_at( &self, subject: &[u8], start: usize, ) -> Result<bool, Error> { assert!( start <= subject.len(), "start ({}) must be <= subject.len() ({})", start, subject.len() ); let options = 0; let mut match_data = self.match_data(); // SAFETY: We don't use any dangerous PCRE2 options. let res = unsafe { match_data.find(&self.code, subject, start, options) }; PoolGuard::put(match_data); res } /// Returns the same as find, but starts the search at the given /// offset. /// /// The significance of the starting point is that it takes the surrounding /// context into consideration. For example, the `\A` anchor can only /// match when `start == 0`. pub fn find_at<'s>( &self, subject: &'s [u8], start: usize, ) -> Result<Option<Match<'s>>, Error> { let mut match_data = self.match_data(); let res = self.find_at_with_match_data(&mut match_data, subject, start); PoolGuard::put(match_data); res } /// Like find_at, but accepts match data instead of acquiring one itself. /// /// This is useful for implementing the iterator, which permits avoiding /// the synchronization overhead of acquiring the match data. #[inline(always)] fn find_at_with_match_data<'s>( &self, match_data: &mut MatchDataPoolGuard<'_>, subject: &'s [u8], start: usize, ) -> Result<Option<Match<'s>>, Error> { assert!( start <= subject.len(), "start ({}) must be <= subject.len() ({})", start, subject.len() ); let options = 0; // SAFETY: We don't use any dangerous PCRE2 options. if unsafe { !match_data.find(&self.code, subject, start, options)? } { return Ok(None); } let ovector = match_data.ovector(); let (s, e) = (ovector[0], ovector[1]); Ok(Some(Match::new(&subject, s, e))) } /// This is like `captures`, but uses /// [`CaptureLocations`](struct.CaptureLocations.html) /// instead of /// [`Captures`](struct.Captures.html) in order to amortize allocations. /// /// To create a `CaptureLocations` value, use the /// `Regex::capture_locations` method. /// /// This returns the overall match if this was successful, which is always /// equivalent to the `0`th capture group. pub fn captures_read<'s>( &self, locs: &mut CaptureLocations, subject: &'s [u8], ) -> Result<Option<Match<'s>>, Error> { self.captures_read_at(locs, subject, 0) } /// Returns the same as `captures_read`, but starts the search at the given /// offset and populates the capture locations given. /// /// The significance of the starting point is that it takes the surrounding /// context into consideration. For example, the `\A` anchor can only /// match when `start == 0`. pub fn captures_read_at<'s>( &self, locs: &mut CaptureLocations, subject: &'s [u8], start: usize, ) -> Result<Option<Match<'s>>, Error> { assert!( start <= subject.len(), "start ({}) must be <= subject.len() ({})", start, subject.len() ); let options = 0; // SAFETY: We don't use any dangerous PCRE2 options. if unsafe { !locs.data.find(&self.code, subject, start, options)? } { return Ok(None); } let ovector = locs.data.ovector(); let (s, e) = (ovector[0], ovector[1]); Ok(Some(Match::new(&subject, s, e))) } } /// Auxiliary methods. impl Regex { /// Returns the original pattern string for this regex. pub fn as_str(&self) -> &str { &self.pattern } /// Returns a sequence of all capturing groups and their names, if present. /// /// The length of the slice returned is always equal to the result of /// `captures_len`, which is the number of capturing groups (including the /// capturing group for the entire pattern). /// /// Each entry in the slice is the name of the corresponding capturing /// group, if one exists. The first capturing group (at index `0`) is /// always unnamed. /// /// Capturing groups are indexed by the order of the opening parenthesis. pub fn capture_names(&self) -> &[Option<String>] { &self.capture_names } /// Returns the number of capturing groups in the pattern. /// /// This is always 1 more than the number of syntactic groups in the /// pattern, since the first group always corresponds to the entire match. pub fn captures_len(&self) -> usize { self.code.capture_count().expect("a valid capture count from PCRE2") } /// Returns an empty set of capture locations that can be reused in /// multiple calls to `captures_read` or `captures_read_at`. pub fn capture_locations(&self) -> CaptureLocations { CaptureLocations { code: Arc::clone(&self.code), data: self.new_match_data(), } } fn match_data(&self) -> MatchDataPoolGuard<'_> { self.match_data.get() } fn new_match_data(&self) -> MatchData { MatchData::new(self.config.match_config.clone(), &self.code) } } /// CaptureLocations is a low level representation of the raw offsets of each /// submatch. /// /// Primarily, this type is useful when using `Regex` APIs such as /// `captures_read`, which permits amortizing the allocation in which capture /// match locations are stored. /// /// In order to build a value of this type, you'll need to call the /// `capture_locations` method on the `Regex` being used to execute the search. /// The value returned can then be reused in subsequent searches. pub struct CaptureLocations { code: Arc<Code>, data: MatchData, } impl Clone for CaptureLocations { fn clone(&self) -> CaptureLocations { CaptureLocations { code: Arc::clone(&self.code), data: MatchData::new(self.data.config().clone(), &self.code), } } } impl std::fmt::Debug for CaptureLocations { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut offsets: Vec<Option<usize>> = vec![]; for &offset in self.data.ovector() { if offset == PCRE2_UNSET { offsets.push(None); } else { offsets.push(Some(offset)); } } write!(f, "CaptureLocations(")?; f.debug_list().entries(offsets).finish()?; write!(f, ")") } } impl CaptureLocations { /// Returns the start and end positions of the Nth capture group. /// /// This returns `None` if `i` is not a valid capture group or if the /// capture group did not match anything. /// /// The positions returned are always byte indices with respect to the /// original subject string matched. #[inline] pub fn get(&self, i: usize) -> Option<(usize, usize)> { let ovec = self.data.ovector(); let s = match ovec.get(i * 2) { None => return None, Some(&s) if s == PCRE2_UNSET => return None, Some(&s) => s, }; let e = match ovec.get(i * 2 + 1) { None => return None, Some(&e) if e == PCRE2_UNSET => return None, Some(&e) => e, }; Some((s, e)) } /// Returns the total number of capturing groups. /// /// This is always at least `1` since every regex has at least `1` /// capturing group that corresponds to the entire match. #[inline] pub fn len(&self) -> usize { self.data.ovector().len() / 2 } } /// Captures represents a group of captured byte strings for a single match. /// /// The 0th capture always corresponds to the entire match. Each subsequent /// index corresponds to the next capture group in the regex. If a capture /// group is named, then the matched byte string is *also* available via the /// `name` method. (Note that the 0th capture is always unnamed and so must be /// accessed with the `get` method.) /// /// Positions returned from a capture group are always byte indices. /// /// `'s` is the lifetime of the matched subject string. pub struct Captures<'s> { subject: &'s [u8], locs: CaptureLocations, idx: Arc<HashMap<String, usize>>, } impl<'s> Captures<'s> { /// Returns the match associated with the capture group at index `i`. If /// `i` does not correspond to a capture group, or if the capture group /// did not participate in the match, then `None` is returned. /// /// # Examples /// /// Get the text of the match with a default of an empty string if this /// group didn't participate in the match: /// /// ```rust /// # fn example() -> Result<(), ::pcre2::Error> { /// use pcre2::bytes::Regex; /// /// let re = Regex::new(r"[a-z]+(?:([0-9]+)|([A-Z]+))")?; /// let caps = re.captures(b"abc123")?.unwrap(); /// /// let text1 = caps.get(1).map_or(&b""[..], |m| m.as_bytes()); /// let text2 = caps.get(2).map_or(&b""[..], |m| m.as_bytes()); /// assert_eq!(text1, &b"123"[..]); /// assert_eq!(text2, &b""[..]); /// # Ok(()) }; example().unwrap() /// ``` pub fn get(&self, i: usize) -> Option<Match<'s>> { self.locs.get(i).map(|(s, e)| Match::new(self.subject, s, e)) } /// Returns the match for the capture group named `name`. If `name` isn't a /// valid capture group or didn't match anything, then `None` is returned. pub fn name(&self, name: &str) -> Option<Match<'s>> { self.idx.get(name).and_then(|&i| self.get(i)) } /// Returns the number of captured groups. /// /// This is always at least `1`, since every regex has at least one capture /// group that corresponds to the full match. #[inline] pub fn len(&self) -> usize { self.locs.len() } } impl<'s> std::fmt::Debug for Captures<'s> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("Captures").field(&CapturesDebug(self)).finish() } } struct CapturesDebug<'c, 's: 'c>(&'c Captures<'s>); impl<'c, 's> std::fmt::Debug for CapturesDebug<'c, 's> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn escape_bytes(bytes: &[u8]) -> String { let mut s = String::new(); for &b in bytes { s.push_str(&escape_byte(b)); } s } fn escape_byte(byte: u8) -> String { use std::ascii::escape_default; let escaped: Vec<u8> = escape_default(byte).collect(); String::from_utf8_lossy(&escaped).into_owned() } // We'd like to show something nice here, even if it means an // allocation to build a reverse index. let slot_to_name: HashMap<&usize, &String> = self.0.idx.iter().map(|(a, b)| (b, a)).collect(); let mut map = f.debug_map(); for slot in 0..self.0.len() { let m = self .0 .locs .get(slot) .map(|(s, e)| escape_bytes(&self.0.subject[s..e])); if let Some(name) = slot_to_name.get(&slot) { map.entry(&name, &m); } else { map.entry(&slot, &m); } } map.finish() } } /// Get a group by index. /// /// `'s` is the lifetime of the matched subject string. /// /// The subject can't outlive the `Captures` object if this method is /// used, because of how `Index` is defined (normally `a[i]` is part /// of `a` and can't outlive it); to do that, use `get()` instead. /// /// # Panics /// /// If there is no group at the given index. impl<'s> std::ops::Index<usize> for Captures<'s> { type Output = [u8]; fn index(&self, i: usize) -> &[u8] { self.get(i) .map(|m| m.as_bytes()) .unwrap_or_else(|| panic!("no group at index '{}'", i)) } } /// Get a group by name. /// /// `'s` is the lifetime of the matched subject string and `'i` is the lifetime /// of the group name (the index). /// /// The text can't outlive the `Captures` object if this method is /// used, because of how `Index` is defined (normally `a[i]` is part /// of `a` and can't outlive it); to do that, use `name` instead. /// /// # Panics /// /// If there is no group named by the given value. impl<'s, 'i> std::ops::Index<&'i str> for Captures<'s> { type Output = [u8]; fn index<'a>(&'a self, name: &'i str) -> &'a [u8] { self.name(name) .map(|m| m.as_bytes()) .unwrap_or_else(|| panic!("no group named '{}'", name)) } } /// An iterator over all non-overlapping matches for a particular subject /// string. /// /// The iterator yields matches (if no error occurred while searching) /// corresponding to the start and end of the match. The indices are byte /// offsets. The iterator stops when no more matches can be found. /// /// `'r` is the lifetime of the compiled regular expression and `'s` is the /// lifetime of the subject string. pub struct Matches<'r, 's> { re: &'r Regex, match_data: MatchDataPoolGuard<'r>, subject: &'s [u8], last_end: usize, last_match: Option<usize>, } impl<'r, 's> Iterator for Matches<'r, 's> { type Item = Result<Match<'s>, Error>; fn next(&mut self) -> Option<Result<Match<'s>, Error>> { if self.last_end > self.subject.len() { return None; } let res = self.re.find_at_with_match_data( &mut self.match_data, self.subject, self.last_end, ); let m = match res { Err(err) => return Some(Err(err)), Ok(None) => return None, Ok(Some(m)) => m, }; if m.start() == m.end() { // This is an empty match. To ensure we make progress, start // the next search at the smallest possible starting position // of the next match following this one. self.last_end = m.end() + 1; // Don't accept empty matches immediately following a match. // Just move on to the next match. if Some(m.end()) == self.last_match { return self.next(); } } else { self.last_end = m.end(); } self.last_match = Some(m.end()); Some(Ok(m)) } } /// An iterator that yields all non-overlapping capture groups matching a /// particular regular expression. /// /// The iterator stops when no more matches can be found. /// /// `'r` is the lifetime of the compiled regular expression and `'s` is the /// lifetime of the subject string. pub struct CaptureMatches<'r, 's> { re: &'r Regex, subject: &'s [u8], last_end: usize, last_match: Option<usize>, } impl<'r, 's> Iterator for CaptureMatches<'r, 's> { type Item = Result<Captures<'s>, Error>; fn next(&mut self) -> Option<Result<Captures<'s>, Error>> { if self.last_end > self.subject.len() { return None; } let mut locs = self.re.capture_locations(); let res = self.re.captures_read_at(&mut locs, self.subject, self.last_end); let m = match res { Err(err) => return Some(Err(err)), Ok(None) => return None, Ok(Some(m)) => m, }; if m.start() == m.end() { // This is an empty match. To ensure we make progress, start // the next search at the smallest possible starting position // of the next match following this one. self.last_end = m.end() + 1; // Don't accept empty matches immediately following a match. // Just move on to the next match. if Some(m.end()) == self.last_match { return self.next(); } } else { self.last_end = m.end(); } self.last_match = Some(m.end()); Some(Ok(Captures { subject: self.subject, locs, idx: Arc::clone(&self.re.capture_names_idx), })) } } /// A type alias for our pool of `MatchData` that fixes the type parameters to /// what we actually use in practice. type MatchDataPool = Pool<MatchData, MatchDataPoolFn>; /// Same as above, but for the guard returned by a pool. type MatchDataPoolGuard<'a> = PoolGuard<'a, MatchData, MatchDataPoolFn>; /// The type of the closure we use to create new caches. We need to spell out /// all of the marker traits or else we risk leaking !MARKER impls. type MatchDataPoolFn = Box<dyn Fn() -> MatchData + Send + Sync + UnwindSafe + RefUnwindSafe>; #[cfg(test)] mod tests { use super::{Regex, RegexBuilder}; use crate::is_jit_available; fn b(string: &str) -> &[u8] { string.as_bytes() } fn find_iter_tuples(re: &Regex, subject: &[u8]) -> Vec<(usize, usize)> { let mut tuples = vec![]; for result in re.find_iter(subject) { let m = result.unwrap(); tuples.push((m.start(), m.end())); } tuples } fn cap_iter_tuples(re: &Regex, subject: &[u8]) -> Vec<(usize, usize)> { let mut tuples = vec![]; for result in re.captures_iter(subject) { let caps = result.unwrap(); let m = caps.get(0).unwrap(); tuples.push((m.start(), m.end())); } tuples } #[test] fn caseless() { let re = RegexBuilder::new().caseless(true).build("a").unwrap(); assert!(re.is_match(b("A")).unwrap()); let re = RegexBuilder::new().caseless(true).ucp(true).build("β").unwrap(); assert!(re.is_match(b("Β")).unwrap()); } #[test] fn crlf() { let re = RegexBuilder::new().crlf(true).build("a$").unwrap(); let m = re.find(b("a\r\n")).unwrap().unwrap(); assert_eq!(m.as_pair(), (0, 1)); } #[test] fn dotall() { let re = RegexBuilder::new().dotall(false).build(".").unwrap(); assert!(!re.is_match(b("\n")).unwrap()); let re = RegexBuilder::new().dotall(true).build(".").unwrap(); assert!(re.is_match(b("\n")).unwrap()); } #[test] fn extended() { let re = RegexBuilder::new().extended(true).build("a b c").unwrap(); assert!(re.is_match(b("abc")).unwrap()); } #[test] fn multi_line() { let re = RegexBuilder::new().multi_line(false).build("^abc$").unwrap(); assert!(!re.is_match(b("foo\nabc\nbar")).unwrap()); let re = RegexBuilder::new().multi_line(true).build("^abc$").unwrap(); assert!(re.is_match(b("foo\nabc\nbar")).unwrap()); } #[test] fn ucp() { let re = RegexBuilder::new().ucp(false).build(r"\w").unwrap(); assert!(!re.is_match(b("β")).unwrap()); let re = RegexBuilder::new().ucp(true).build(r"\w").unwrap(); assert!(re.is_match(b("β")).unwrap()); } #[test] fn utf() { let re = RegexBuilder::new().utf(false).build(".").unwrap(); assert_eq!(re.find(b("β")).unwrap().unwrap().as_pair(), (0, 1)); let re = RegexBuilder::new().utf(true).build(".").unwrap(); assert_eq!(re.find(b("β")).unwrap().unwrap().as_pair(), (0, 2)); } #[test] fn jit4lyfe() { if is_jit_available() { let re = RegexBuilder::new().jit(true).build(r"\w").unwrap(); assert!(re.is_match(b("a")).unwrap()); } else { // Check that if JIT isn't enabled, then we get an error if we // require JIT. RegexBuilder::new().jit(true).build(r"\w").unwrap_err(); } } // Unlike jit4lyfe, this tests that everything works when requesting the // JIT only if it's available. In jit4lyfe, we require the JIT or fail. // If the JIT isn't available, then in this test, we simply don't use it. #[test] fn jit_if_available() { let re = RegexBuilder::new().jit_if_available(true).build(r"\w").unwrap(); assert!(re.is_match(b("a")).unwrap()); } // This tests a regression caused a segfault in the pcre2 library // https://github.com/BurntSushi/rust-pcre2/issues/10 #[test] fn jit_test_lazy_alloc_subject() { let subject: Vec<u8> = vec![]; let re = RegexBuilder::new() .jit_if_available(true) .build(r"xxxx|xxxx|xxxx") .unwrap(); assert!(!re.is_match(&subject).unwrap()); } #[test] fn utf_with_invalid_data() { let re = RegexBuilder::new().build(r".").unwrap(); assert_eq!(re.find(b"\xFF").unwrap().unwrap().as_pair(), (0, 1)); let re = RegexBuilder::new().utf(true).build(r".").unwrap(); assert!(re.find(b"\xFF").is_err()); } #[test] fn capture_names() { let re = RegexBuilder::new() .build(r"(?P<foo>abc)|(def)|(?P<a>ghi)|(?P<springsteen>jkl)") .unwrap(); assert_eq!( re.capture_names().to_vec(), vec![ None, Some("foo".to_string()), None, Some("a".to_string()), Some("springsteen".to_string()), ] ); // Test our internal map as well. assert_eq!(re.capture_names_idx.len(), 3); assert_eq!(re.capture_names_idx["foo"], 1); assert_eq!(re.capture_names_idx["a"], 3); assert_eq!(re.capture_names_idx["springsteen"], 4); } #[test] fn captures_get() { let re = Regex::new(r"[a-z]+(?:([0-9]+)|([A-Z]+))").unwrap(); let caps = re.captures(b"abc123").unwrap().unwrap(); let text1 = caps.get(1).map_or(&b""[..], |m| m.as_bytes()); let text2 = caps.get(2).map_or(&b""[..], |m| m.as_bytes()); assert_eq!(text1, &b"123"[..]); assert_eq!(text2, &b""[..]); } #[test] fn find_iter_empty() { let re = Regex::new(r"(?m:^)").unwrap(); assert_eq!(find_iter_tuples(&re, b""), vec![(0, 0)]); assert_eq!(find_iter_tuples(&re, b"\n"), vec![(0, 0)]); assert_eq!(find_iter_tuples(&re, b"\n\n"), vec![(0, 0), (1, 1)]); assert_eq!(find_iter_tuples(&re, b"\na\n"), vec![(0, 0), (1, 1)]); assert_eq!( find_iter_tuples(&re, b"\na\n\n"), vec![(0, 0), (1, 1), (3, 3),] ); } #[test] fn captures_iter_empty() { let re = Regex::new(r"(?m:^)").unwrap(); assert_eq!(cap_iter_tuples(&re, b""), vec![(0, 0)]); assert_eq!(cap_iter_tuples(&re, b"\n"), vec![(0, 0)]); assert_eq!(cap_iter_tuples(&re, b"\n\n"), vec![(0, 0), (1, 1)]); assert_eq!(cap_iter_tuples(&re, b"\na\n"), vec![(0, 0), (1, 1)]); assert_eq!( cap_iter_tuples(&re, b"\na\n\n"), vec![(0, 0), (1, 1), (3, 3),] ); } #[test] fn max_jit_stack_size_does_something() { if !is_jit_available() { return; } let hundred = "\ ABCDEFGHIJKLMNOPQRSTUVWXY\ ABCDEFGHIJKLMNOPQRSTUVWXY\ ABCDEFGHIJKLMNOPQRSTUVWXY\ ABCDEFGHIJKLMNOPQRSTUVWXY\ "; let hay = format!("{}", hundred.repeat(100)); // First, try a regex that checks that we can blow the JIT stack limit. let re = RegexBuilder::new() .ucp(true) .jit(true) .max_jit_stack_size(Some(1)) .build(r"((((\w{10})){100}))+") .unwrap(); let result = re.is_match(hay.as_bytes()); if result.is_ok() { // Skip this test, since for some reason we weren't able to blow // the stack limit. return; } let err = result.unwrap_err(); assert!(err.to_string().contains("JIT stack limit reached")); // Now bump up the JIT stack limit and check that it succeeds. let re = RegexBuilder::new() .ucp(true) .jit(true) .max_jit_stack_size(Some(1 << 20)) .build(r"((((\w{10})){100}))+") .unwrap(); assert!(re.is_match(hay.as_bytes()).unwrap()); } #[test] fn find_start_end_and_as_bytes() { let hay = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; let pattern = r" (?x) (?#: Allow comments and whitespace.) [a-z] (?#: Lowercase letter.) + (?#: One or more times.) "; let re = RegexBuilder::new() .extended(true) .utf(true) .jit_if_available(true) .build(pattern) .unwrap(); let matched = re.find(hay.as_bytes()).unwrap().unwrap(); assert_eq!(matched.start(), 10); assert_eq!(matched.end(), 10 + 26); assert_eq!(matched.as_bytes(), b"abcdefghijklmnopqrstuvwxyz"); } #[test] fn find_utf_emoji_as_bytes() { let hay = "0123456789😀👍🏼🎉abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; let pattern = r"(*UTF) (?x) (?#: Allow comments and whitespace.) [^\N{U+0000}-\N{U+007F}] (?#: Non-ascii code points.) + (?#: One or more times.) "; let re = RegexBuilder::new() .extended(true) .utf(true) .jit_if_available(true) .build(pattern) .unwrap(); let matched = re.find(hay.as_bytes()).unwrap().unwrap(); assert_eq!(matched.as_bytes(), "😀👍🏼🎉".as_bytes()); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pcre2-0.2.9/src/error.rs����������������������������������������������������������������������������0000644�0000000�0000000�00000012126�10461020230�0013137�0����������������������������������������������������������������������������������������������������ustar �����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use { libc::c_int, pcre2_sys::{ pcre2_get_error_message_8, PCRE2_ERROR_BADDATA, PCRE2_ERROR_NOMEMORY, }, }; /// A PCRE2 error. /// /// An error can occur during compilation or during matching. The kind of this /// error indicates the type of operation being performed when the error /// occurred. #[derive(Clone)] pub struct Error { kind: ErrorKind, code: c_int, offset: Option<usize>, } /// The kind of an error indicates the type of operation that was attempted /// that resulted in an error. /// /// This enum may expand over time. #[derive(Clone, Debug)] #[non_exhaustive] pub enum ErrorKind { /// An error occurred during compilation of a regex. Compile, /// An error occurred during JIT compilation of a regex. JIT, /// An error occurred while matching. Match, /// An error occurred while querying a compiled regex for info. Info, /// An error occurred while setting an option. Option, } impl Error { /// Create a new compilation error. pub(crate) fn compile(code: c_int, offset: usize) -> Error { Error { kind: ErrorKind::Compile, code, offset: Some(offset) } } /// Create a new JIT compilation error. pub(crate) fn jit(code: c_int) -> Error { Error { kind: ErrorKind::JIT, code, offset: None } } /// Create a new matching error. pub(crate) fn matching(code: c_int) -> Error { Error { kind: ErrorKind::Match, code, offset: None } } /// Create a new info error. pub(crate) fn info(code: c_int) -> Error { Error { kind: ErrorKind::Info, code, offset: None } } /// Create a new option error. pub(crate) fn option(code: c_int) -> Error { Error { kind: ErrorKind::Option, code, offset: None } } /// Return the kind of this error. /// /// The kind indicates the type of operation that was attempted which /// resulted in this error. pub fn kind(&self) -> &ErrorKind { &self.kind } /// Return the raw underlying PCRE2 error code. /// /// This can be useful if one needs to determine exactly which error /// occurred, which can be done with case analysis over the constants /// exported in the `pcre2-sys` crate. pub fn code(&self) -> c_int { self.code } /// Return the underlying offset associated with this error, if one exists. /// /// The offset is typically only available for compile time errors, and /// is supposed to indicate the general position in the pattern where an /// error occurred. pub fn offset(&self) -> Option<usize> { self.offset } /// Returns the error message from PCRE2. fn error_message(&self) -> String { // PCRE2 docs say a buffer size of 120 bytes is enough, but we're // cautious and double it. let mut buf = [0u8; 240]; let rc = unsafe { pcre2_get_error_message_8(self.code, buf.as_mut_ptr(), buf.len()) }; // Errors are only ever constructed from codes reported by PCRE2, so // our code should always be valid. assert!(rc != PCRE2_ERROR_BADDATA, "used an invalid error code"); // PCRE2 docs claim 120 bytes is enough, and we use more, so... assert!(rc != PCRE2_ERROR_NOMEMORY, "buffer size too small"); // Sanity check that we do indeed have a non-negative result. 0 is OK. assert!(rc >= 0, "expected non-negative but got {}", rc); String::from_utf8(buf[..rc as usize].to_vec()).expect("valid UTF-8") } } impl std::error::Error for Error { fn description(&self) -> &str { "pcre2 error" } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let msg = self.error_message(); match self.kind { ErrorKind::Compile => match self.offset { None => { write!(f, "PCRE2: error compiling pattern: {}", msg) } Some(offset) => { write!( f, "PCRE2: error compiling pattern at offset {}: {}", offset, msg ) } }, ErrorKind::JIT => { write!(f, "PCRE2: error JIT compiling pattern: {}", msg) } ErrorKind::Match => { write!(f, "PCRE2: error matching: {}", msg) } ErrorKind::Info => { write!(f, "PCRE2: error getting info: {}", msg) } ErrorKind::Option => { write!(f, "PCRE2: error setting option: {}", msg) } } } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { // We include the error message in the debug representation since // most humans probably don't have PCRE2 error codes memorized. f.debug_struct("Error") .field("kind", &self.kind) .field("code", &self.code) .field("offset", &self.offset) .field("message", &self.error_message()) .finish() } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pcre2-0.2.9/src/ffi.rs������������������������������������������������������������������������������0000644�0000000�0000000�00000042764�10461020230�0012565�0����������������������������������������������������������������������������������������������������ustar �����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! This module defines a low level and *mostly* safe abstraction around the core PCRE2 regex primitives. Callers may still need to deal with some unsafety, but this layer will take care of the obvious things, such as resource management and error handling. */ use std::{cmp, ptr, slice}; use {libc::c_void, pcre2_sys::*}; use crate::error::Error; /// Returns true if and only if PCRE2 believes that JIT is available. pub fn is_jit_available() -> bool { let mut rc: u32 = 0; let error_code = unsafe { pcre2_config_8(PCRE2_CONFIG_JIT, &mut rc as *mut _ as *mut c_void) }; if error_code < 0 { // If PCRE2_CONFIG_JIT is a bad option, then there's a bug somewhere. panic!("BUG: {}", Error::jit(error_code)); } rc == 1 } /// Returns the version of PCRE2 being used. /// /// The tuple returned corresponds to the major and minor version, e.g., /// `(10, 32)`. pub fn version() -> (u32, u32) { (PCRE2_MAJOR, PCRE2_MINOR) } /// Escapes all regular expression meta characters in `pattern`. /// /// The string returned may be safely used as a literal in a regular /// expression. pub fn escape(pattern: &str) -> String { fn is_meta_character(c: char) -> bool { match c { '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '-' => true, _ => false, } } // Is it really true that PCRE2 doesn't have an API routine to // escape a pattern so that it matches literally? Wow. I couldn't // find one. It does of course have \Q...\E, but, umm, what if the // literal contains a \E? let mut quoted = String::new(); quoted.reserve(pattern.len()); for c in pattern.chars() { if is_meta_character(c) { quoted.push('\\'); } quoted.push(c); } quoted } /// A low level representation of a compiled PCRE2 code object. pub(crate) struct Code { code: *mut pcre2_code_8, compiled_jit: bool, // We hang on to this but don't use it so that it gets freed when the // compiled code gets freed. It's not clear whether this is necessary or // not, but presumably doesn't cost us much to be conservative. #[allow(dead_code)] ctx: CompileContext, } // SAFETY: Compiled PCRE2 code objects are immutable once built and explicitly // safe to use from multiple threads simultaneously. // // One hitch here is that JIT compiling can write into a PCRE2 code object, but // we only ever JIT compile immediately after first building the code object // and before making it available to the caller. unsafe impl Send for Code {} unsafe impl Sync for Code {} impl Drop for Code { fn drop(&mut self) { unsafe { pcre2_code_free_8(self.code) } } } impl Code { /// Compile the given pattern with the given options. If there was a /// problem compiling the pattern, then return an error. pub(crate) fn new( pattern: &str, options: u32, mut ctx: CompileContext, ) -> Result<Code, Error> { let (mut error_code, mut error_offset) = (0, 0); let code = unsafe { pcre2_compile_8( pattern.as_ptr(), pattern.len(), options, &mut error_code, &mut error_offset, ctx.as_mut_ptr(), ) }; if code.is_null() { Err(Error::compile(error_code, error_offset)) } else { Ok(Code { code, compiled_jit: false, ctx }) } } /// JIT compile this code object. /// /// If there was a problem performing JIT compilation, then this returns /// an error. pub(crate) fn jit_compile(&mut self) -> Result<(), Error> { let error_code = unsafe { pcre2_jit_compile_8(self.code, PCRE2_JIT_COMPLETE) }; if error_code == 0 { self.compiled_jit = true; Ok(()) } else { Err(Error::jit(error_code)) } } /// Build and return an ordered sequence of all capture group names in this /// compiled regex. /// /// The returned vector has a slot for every capturing group (including the /// one corresponding to the entire regex, which is always unnamed). Groups /// that are unnamed are set to `None`. /// /// If there was a problem querying the compiled object for information, /// then this returns an error. pub(crate) fn capture_names(&self) -> Result<Vec<Option<String>>, Error> { // This is an object lesson in why C sucks. All we need is a map from // a name to a number, but we need to go through all sorts of // shenanigans to get it. In order to verify this code, see // https://www.pcre.org/current/doc/html/pcre2api.html // and search for PCRE2_INFO_NAMETABLE. let name_count = self.name_count()?; let size = self.name_entry_size()?; let table = unsafe { slice::from_raw_parts(self.raw_name_table()?, name_count * size) }; let mut names = vec![None; self.capture_count()?]; for i in 0..name_count { let entry = &table[i * size..(i + 1) * size]; let name = &entry[2..]; let nulat = name .iter() .position(|&b| b == 0) .expect("a NUL in name table entry"); let index = (entry[0] as usize) << 8 | (entry[1] as usize); names[index] = String::from_utf8(name[..nulat].to_vec()) .map(Some) // We require our pattern to be valid UTF-8, so all capture // names should also be valid UTF-8. .expect("valid UTF-8 for capture name"); } Ok(names) } /// Return the underlying raw pointer to the code object. pub(crate) fn as_ptr(&self) -> *const pcre2_code_8 { self.code } /// Returns the raw name table, where each entry in the table corresponds /// to a mapping between a named capturing group and the index of that /// capturing group. The encoding for each item is as follows: /// /// * 2 bytes encoding the capture index (big-endian) /// * N bytes encoding the code units of the name /// * 1 byte for the NUL terminator /// * M padding bytes, corresponding to the difference in length between /// this name and the longest name. /// /// In particular, each entry uses the same number of bytes. /// /// Entries are in alphabetical order. fn raw_name_table(&self) -> Result<*const u8, Error> { let mut bytes: *const u8 = ptr::null(); let rc = unsafe { pcre2_pattern_info_8( self.as_ptr(), PCRE2_INFO_NAMETABLE, &mut bytes as *mut *const u8 as *mut c_void, ) }; if rc != 0 { Err(Error::info(rc)) } else { Ok(bytes) } } /// Returns the number of named capturing groups. fn name_count(&self) -> Result<usize, Error> { let mut count: u32 = 0; let rc = unsafe { pcre2_pattern_info_8( self.as_ptr(), PCRE2_INFO_NAMECOUNT, &mut count as *mut u32 as *mut c_void, ) }; if rc != 0 { Err(Error::info(rc)) } else { Ok(count as usize) } } /// Returns the entry size of each name in the name table. /// /// This appears to correspond to `3` plus the size of the longest named /// capturing group. The extra 3 bytes correspond to a NUL terminator and /// two prefix bytes corresponding to a big-endian encoding of the index /// of the capture group. fn name_entry_size(&self) -> Result<usize, Error> { let mut size: u32 = 0; let rc = unsafe { pcre2_pattern_info_8( self.as_ptr(), PCRE2_INFO_NAMEENTRYSIZE, &mut size as *mut u32 as *mut c_void, ) }; if rc != 0 { Err(Error::info(rc)) } else { Ok(size as usize) } } /// Returns the total number of capturing groups in this regex. This /// includes the capturing group for the entire pattern, so that this is /// always 1 more than the number of syntactic groups in the pattern. pub(crate) fn capture_count(&self) -> Result<usize, Error> { let mut count: u32 = 0; let rc = unsafe { pcre2_pattern_info_8( self.as_ptr(), PCRE2_INFO_CAPTURECOUNT, &mut count as *mut u32 as *mut c_void, ) }; if rc != 0 { Err(Error::info(rc)) } else { Ok(1 + count as usize) } } } /// A low level representation of PCRE2's compilation context. pub(crate) struct CompileContext(*mut pcre2_compile_context_8); // SAFETY: Compile contexts are safe to read from multiple threads // simultaneously. No interior mutability is used, so Sync is safe. unsafe impl Send for CompileContext {} unsafe impl Sync for CompileContext {} impl Drop for CompileContext { fn drop(&mut self) { unsafe { pcre2_compile_context_free_8(self.0) } } } impl CompileContext { /// Create a new empty compilation context. /// /// If memory could not be allocated for the context, then this panics. pub(crate) fn new() -> CompileContext { let ctx = unsafe { pcre2_compile_context_create_8(ptr::null_mut()) }; assert!(!ctx.is_null(), "could not allocate compile context"); CompileContext(ctx) } /// Set the PCRE2 newline sequence. /// /// Valid values are: PCRE2_NEWLINE_CR, PCRE2_NEWLINE_LF, /// PCRE2_NEWLINE_CRLF, PCRE2_NEWLINE_ANYCRLF, PCRE2_NEWLINE_ANY or /// PCRE2_NEWLINE_NUL. Using any other value results in an error. pub(crate) fn set_newline(&mut self, value: u32) -> Result<(), Error> { let rc = unsafe { pcre2_set_newline_8(self.0, value) }; if rc == 0 { Ok(()) } else { Err(Error::option(rc)) } } fn as_mut_ptr(&mut self) -> *mut pcre2_compile_context_8 { self.0 } } /// Configuration for PCRE2's match context. #[derive(Clone, Debug)] pub(crate) struct MatchConfig { /// When set, a custom JIT stack will be created with the given maximum /// size. pub(crate) max_jit_stack_size: Option<usize>, } impl Default for MatchConfig { fn default() -> MatchConfig { MatchConfig { max_jit_stack_size: None } } } /// A low level representation of a match data block. /// /// Technically, a single match data block can be used with multiple regexes /// (not simultaneously), but in practice, we just create a single match data /// block for each regex for each thread it's used in. pub(crate) struct MatchData { config: MatchConfig, match_context: *mut pcre2_match_context_8, match_data: *mut pcre2_match_data_8, jit_stack: Option<*mut pcre2_jit_stack_8>, ovector_ptr: *const usize, ovector_count: u32, } // SAFETY: Match data blocks can be freely sent from one thread to another, // but they do not support multiple threads using them simultaneously. We still // implement Sync however, since we require mutable access to use the match // data block for executing a search, which statically prevents simultaneous // reading/writing. It is legal to read match data blocks from multiple threads // simultaneously. unsafe impl Send for MatchData {} unsafe impl Sync for MatchData {} impl Drop for MatchData { fn drop(&mut self) { unsafe { if let Some(stack) = self.jit_stack { pcre2_jit_stack_free_8(stack); } pcre2_match_data_free_8(self.match_data); pcre2_match_context_free_8(self.match_context); } } } impl MatchData { /// Create a new match data block from a compiled PCRE2 code object. /// /// This panics if memory could not be allocated for the block. pub(crate) fn new(config: MatchConfig, code: &Code) -> MatchData { let match_context = unsafe { pcre2_match_context_create_8(ptr::null_mut()) }; assert!(!match_context.is_null(), "failed to allocate match context"); let match_data = unsafe { pcre2_match_data_create_from_pattern_8( code.as_ptr(), ptr::null_mut(), ) }; assert!(!match_data.is_null(), "failed to allocate match data block"); let jit_stack = match config.max_jit_stack_size { None => None, Some(_) if !code.compiled_jit => None, Some(max) => { let stack = unsafe { pcre2_jit_stack_create_8( cmp::min(max, 32 * 1 << 10), max, ptr::null_mut(), ) }; assert!(!stack.is_null(), "failed to allocate JIT stack"); unsafe { pcre2_jit_stack_assign_8( match_context, None, stack as *mut c_void, ) }; Some(stack) } }; let ovector_ptr = unsafe { pcre2_get_ovector_pointer_8(match_data) }; assert!(!ovector_ptr.is_null(), "got NULL ovector pointer"); let ovector_count = unsafe { pcre2_get_ovector_count_8(match_data) }; MatchData { config, match_context, match_data, jit_stack, ovector_ptr, ovector_count, } } /// Return the configuration for this match data object. pub(crate) fn config(&self) -> &MatchConfig { &self.config } /// Execute PCRE2's primary match routine on the given subject string /// starting at the given offset. The provided options are passed to PCRE2 /// as is. /// /// This returns false if no match occurred. /// /// Match offsets can be extracted via `ovector`. /// /// # Safety /// /// This routine is marked unsafe because it allows the caller to set /// arbitrary PCRE2 options. Some of those options can invoke undefined /// behavior when not used correctly. For example, if PCRE2_NO_UTF_CHECK /// is given and UTF mode is enabled and the given subject string is not /// valid UTF-8, then the result is undefined. pub(crate) unsafe fn find( &mut self, code: &Code, mut subject: &[u8], start: usize, options: u32, ) -> Result<bool, Error> { // When the subject is empty, we use an NON-empty slice with a known // valid pointer. Otherwise, slices derived from, e.g., an empty // `Vec<u8>` may not have a valid pointer, since creating an empty // `Vec` is guaranteed to not allocate. // // We use a non-empty slice since it is otherwise difficult // to guarantee getting a dereferencable pointer. Which makes // sense, because the slice is empty, the pointer should never be // dereferenced! // // Alas, older versions of PCRE2 did exactly this. While that bug has // been fixed a while ago, it still seems to pop up[1]. So we try // harder. // // Note that even though we pass a non-empty slice in this case, we // still pass a length of zero. This just provides a pointer that won't // explode if you try to dereference it. // // [1]: https://github.com/BurntSushi/rust-pcre2/issues/42 static SINGLETON: &[u8] = &[0]; let len = subject.len(); if subject.is_empty() { subject = SINGLETON; } let rc = pcre2_match_8( code.as_ptr(), subject.as_ptr(), len, start, options, self.as_mut_ptr(), self.match_context, ); if rc == PCRE2_ERROR_NOMATCH { Ok(false) } else if rc > 0 { Ok(true) } else { // We always create match data with // pcre2_match_data_create_from_pattern, so the ovector should // always be big enough. assert!(rc != 0, "ovector should never be too small"); Err(Error::matching(rc)) } } /// Return a mutable reference to the underlying match data. fn as_mut_ptr(&mut self) -> *mut pcre2_match_data_8 { self.match_data } /// Return the ovector corresponding to this match data. /// /// The ovector represents match offsets as pairs. This always returns /// N + 1 pairs (so 2*N + 1 offsets), where N is the number of capturing /// groups in the original regex. pub(crate) fn ovector(&self) -> &[usize] { // SAFETY: Both our ovector pointer and count are derived directly from // the creation of a valid match data block. One interesting question // here is whether the contents of the ovector are always initialized. // The PCRE2 documentation suggests that they are (so does testing), // but this isn't actually 100% clear! unsafe { slice::from_raw_parts( self.ovector_ptr, // This could in theory overflow, but the ovector count comes // directly from PCRE2, so presumably it's guaranteed to never // overflow size_t/usize. Also, in practice, this would require // a number of capture groups so large as to be probably // impossible. self.ovector_count as usize * 2, ) } } } ������������pcre2-0.2.9/src/lib.rs������������������������������������������������������������������������������0000644�0000000�0000000�00000001200�10461020230�0012543�0����������������������������������������������������������������������������������������������������ustar �����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! This crate provides a safe high level Rust binding to [PCRE2](https://www.pcre.org/). The API of this crate attempts to correspond closely to the API of Rust's [`regex`](https://docs.rs/regex) crate. The API provided by this crate neither matches the full API of Rust's regex crate nor does it expose the full functionality of PCRE2. Contributions are welcome to improve this. */ #![deny(missing_docs)] extern crate alloc; pub use crate::{ error::{Error, ErrorKind}, ffi::{escape, is_jit_available, version}, }; /** PCRE2 regular expressions for matching on arbitrary bytes. */ pub mod bytes; mod error; mod ffi; mod pool; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������pcre2-0.2.9/src/pool.rs�����������������������������������������������������������������������������0000644�0000000�0000000�00000111473�10461020230�0012764�0����������������������������������������������������������������������������������������������������ustar �����������������������������������������������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This was essentially copied wholesale from the regex-automata crate. Such // things are quite dubious to do, but it didn't seem appropriate to depend // on regex-automata here. And the code below is not worth putting into a // micro-crate IMO. We could depend on the `thread_local` crate (and we did // at one point), but its memory usage scales with the total number of active // threads that have ever run a regex search, where as the pool below should // only scale with the number of simultaneous regex searches. In practice, it // has been observed that `thread_local` leads to enormous memory usage in // managed runtimes. /*! A thread safe memory pool. The principal type in this module is a [`Pool`]. It main use case is for holding a thread safe collection of mutable scratch spaces (usually called `Cache` in this crate) that PCRE2 needs to execute a search. This avoids needing to re-create the scratch space for every search, which could wind up being quite expensive. */ /// A thread safe pool. /// /// Getting a value out comes with a guard. When that guard is dropped, the /// value is automatically put back in the pool. The guard provides both a /// `Deref` and a `DerefMut` implementation for easy access to an underlying /// `T`. /// /// A `Pool` impls `Sync` when `T` is `Send` (even if `T` is not `Sync`). This /// is possible because a pool is guaranteed to provide a value to exactly one /// thread at any time. /// /// Currently, a pool never contracts in size. Its size is proportional to the /// maximum number of simultaneous uses. This may change in the future. /// /// A `Pool` is a particularly useful data structure for this crate because /// PCRE2 requires a mutable "cache" in order to execute a search. Since /// regexes themselves tend to be global, the problem is then: how do you get a /// mutable cache to execute a search? You could: /// /// 1. Use a `thread_local!`, which requires the standard library and requires /// that the regex pattern be statically known. /// 2. Use a `Pool`. /// 3. Make the cache an explicit dependency in your code and pass it around. /// 4. Put the cache state in a `Mutex`, but this means only one search can /// execute at a time. /// 5. Create a new cache for every search. /// /// A `thread_local!` is perhaps the best choice if it works for your use case. /// Putting the cache in a mutex or creating a new cache for every search are /// perhaps the worst choices. Of the remaining two choices, whether you use /// this `Pool` or thread through a cache explicitly in your code is a matter /// of taste and depends on your code architecture. pub(crate) struct Pool<T, F = fn() -> T>(alloc::boxed::Box<inner::Pool<T, F>>); impl<T, F> Pool<T, F> { /// Create a new pool. The given closure is used to create values in /// the pool when necessary. pub(crate) fn new(create: F) -> Pool<T, F> { Pool(alloc::boxed::Box::new(inner::Pool::new(create))) } } impl<T: Send, F: Fn() -> T> Pool<T, F> { /// Get a value from the pool. The caller is guaranteed to have /// exclusive access to the given value. Namely, it is guaranteed that /// this will never return a value that was returned by another call to /// `get` but was not put back into the pool. /// /// When the guard goes out of scope and its destructor is called, then /// it will automatically be put back into the pool. Alternatively, /// [`PoolGuard::put`] may be used to explicitly put it back in the pool /// without relying on its destructor. /// /// Note that there is no guarantee provided about which value in the /// pool is returned. That is, calling get, dropping the guard (causing /// the value to go back into the pool) and then calling get again is /// *not* guaranteed to return the same value received in the first `get` /// call. #[inline] pub(crate) fn get(&self) -> PoolGuard<'_, T, F> { PoolGuard(self.0.get()) } } impl<T: core::fmt::Debug, F> core::fmt::Debug for Pool<T, F> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_tuple("Pool").field(&self.0).finish() } } /// A guard that is returned when a caller requests a value from the pool. /// /// The purpose of the guard is to use RAII to automatically put the value /// back in the pool once it's dropped. pub struct PoolGuard<'a, T: Send, F: Fn() -> T>(inner::PoolGuard<'a, T, F>); impl<'a, T: Send, F: Fn() -> T> PoolGuard<'a, T, F> { /// Consumes this guard and puts it back into the pool. /// /// This circumvents the guard's `Drop` implementation. This can be useful /// in circumstances where the automatic `Drop` results in poorer codegen, /// such as calling non-inlined functions. #[inline] pub(crate) fn put(this: PoolGuard<'_, T, F>) { inner::PoolGuard::put(this.0); } } impl<'a, T: Send, F: Fn() -> T> core::ops::Deref for PoolGuard<'a, T, F> { type Target = T; #[inline] fn deref(&self) -> &T { self.0.value() } } impl<'a, T: Send, F: Fn() -> T> core::ops::DerefMut for PoolGuard<'a, T, F> { #[inline] fn deref_mut(&mut self) -> &mut T { self.0.value_mut() } } impl<'a, T: Send + core::fmt::Debug, F: Fn() -> T> core::fmt::Debug for PoolGuard<'a, T, F> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_tuple("PoolGuard").field(&self.0).finish() } } mod inner { use core::{ cell::UnsafeCell, panic::{RefUnwindSafe, UnwindSafe}, sync::atomic::{AtomicUsize, Ordering}, }; use alloc::vec; use std::{sync::Mutex, thread_local}; /// An atomic counter used to allocate thread IDs. /// /// We specifically start our counter at 3 so that we can use the values /// less than it as sentinels. static COUNTER: AtomicUsize = AtomicUsize::new(3); /// A thread ID indicating that there is no owner. This is the initial /// state of a pool. Once a pool has an owner, there is no way to change /// it. static THREAD_ID_UNOWNED: usize = 0; /// A thread ID indicating that the special owner value is in use and not /// available. This state is useful for avoiding a case where the owner /// of a pool calls `get` before putting the result of a previous `get` /// call back into the pool. static THREAD_ID_INUSE: usize = 1; /// This sentinel is used to indicate that a guard has already been dropped /// and should not be re-dropped. We use this because our drop code can be /// called outside of Drop and thus there could be a bug in the internal /// implementation that results in trying to put the same guard back into /// the same pool multiple times, and *that* could result in UB if we /// didn't mark the guard as already having been put back in the pool. /// /// So this isn't strictly necessary, but this let's us define some /// routines as safe (like PoolGuard::put_imp) that we couldn't otherwise /// do. static THREAD_ID_DROPPED: usize = 2; /// The number of stacks we use inside of the pool. These are only used for /// non-owners. That is, these represent the "slow" path. /// /// In the original implementation of this pool, we only used a single /// stack. While this might be okay for a couple threads, the prevalence of /// 32, 64 and even 128 core CPUs has made it untenable. The contention /// such an environment introduces when threads are doing a lot of searches /// on short haystacks (a not uncommon use case) is palpable and leads to /// huge slowdowns. /// /// This constant reflects a change from using one stack to the number of /// stacks that this constant is set to. The stack for a particular thread /// is simply chosen by `thread_id % MAX_POOL_STACKS`. The idea behind /// this setup is that there should be a good chance that accesses to the /// pool will be distributed over several stacks instead of all of them /// converging to one. /// /// This is not a particularly smart or dynamic strategy. Fixing this to a /// specific number has at least two downsides. First is that it will help, /// say, an 8 core CPU more than it will a 128 core CPU. (But, crucially, /// it will still help the 128 core case.) Second is that this may wind /// up being a little wasteful with respect to memory usage. Namely, if a /// regex is used on one thread and then moved to another thread, then it /// could result in creating a new copy of the data in the pool even though /// only one is actually needed. /// /// And that memory usage bit is why this is set to 8 and not, say, 64. /// Keeping it at 8 limits, to an extent, how much unnecessary memory can /// be allocated. /// /// In an ideal world, we'd be able to have something like this: /// /// * Grow the number of stacks as the number of concurrent callers /// increases. I spent a little time trying this, but even just adding an /// atomic addition/subtraction for each pop/push for tracking concurrent /// callers led to a big perf hit. Since even more work would seemingly be /// required than just an addition/subtraction, I abandoned this approach. /// * The maximum amount of memory used should scale with respect to the /// number of concurrent callers and *not* the total number of existing /// threads. This is primarily why the `thread_local` crate isn't used, as /// as some environments spin up a lot of threads. This led to multiple /// reports of extremely high memory usage (often described as memory /// leaks). /// * Even more ideally, the pool should contract in size. That is, it /// should grow with bursts and then shrink. But this is a pretty thorny /// issue to tackle and it might be better to just not. /// * It would be nice to explore the use of, say, a lock-free stack /// instead of using a mutex to guard a `Vec` that is ultimately just /// treated as a stack. The main thing preventing me from exploring this /// is the ABA problem. The `crossbeam` crate has tools for dealing with /// this sort of problem (via its epoch based memory reclamation strategy), /// but I can't justify bringing in all of `crossbeam` as a dependency of /// `regex` for this. /// /// See this issue for more context and discussion: /// https://github.com/rust-lang/regex/issues/934 const MAX_POOL_STACKS: usize = 8; thread_local!( /// A thread local used to assign an ID to a thread. static THREAD_ID: usize = { let next = COUNTER.fetch_add(1, Ordering::Relaxed); // SAFETY: We cannot permit the reuse of thread IDs since reusing a // thread ID might result in more than one thread "owning" a pool, // and thus, permit accessing a mutable value from multiple threads // simultaneously without synchronization. The intent of this panic // is to be a sanity check. It is not expected that the thread ID // space will actually be exhausted in practice. Even on a 32-bit // system, it would require spawning 2^32 threads (although they // wouldn't all need to run simultaneously, so it is in theory // possible). // // This checks that the counter never wraps around, since atomic // addition wraps around on overflow. if next == 0 { panic!("regex: thread ID allocation space exhausted"); } next }; ); /// This puts each stack in the pool below into its own cache line. This is /// an absolutely critical optimization that tends to have the most impact /// in high contention workloads. Without forcing each mutex protected /// into its own cache line, high contention exacerbates the performance /// problem by causing "false sharing." By putting each mutex in its own /// cache-line, we avoid the false sharing problem and the affects of /// contention are greatly reduced. #[derive(Debug)] #[repr(C, align(64))] struct CacheLine<T>(T); /// A thread safe pool utilizing std-only features. /// /// The main difference between this and the simplistic alloc-only pool is /// the use of std::sync::Mutex and an "owner thread" optimization that /// makes accesses by the owner of a pool faster than all other threads. /// This makes the common case of running a regex within a single thread /// faster by avoiding mutex unlocking. pub(super) struct Pool<T, F> { /// A function to create more T values when stack is empty and a caller /// has requested a T. create: F, /// Multiple stacks of T values to hand out. These are used when a Pool /// is accessed by a thread that didn't create it. /// /// Conceptually this is `Mutex<Vec<Box<T>>>`, but sharded out to make /// it scale better under high contention work-loads. We index into /// this sequence via `thread_id % stacks.len()`. stacks: Vec<CacheLine<Mutex<Vec<Box<T>>>>>, /// The ID of the thread that owns this pool. The owner is the thread /// that makes the first call to 'get'. When the owner calls 'get', it /// gets 'owner_val' directly instead of returning a T from 'stack'. /// See comments elsewhere for details, but this is intended to be an /// optimization for the common case that makes getting a T faster. /// /// It is initialized to a value of zero (an impossible thread ID) as a /// sentinel to indicate that it is unowned. owner: AtomicUsize, /// A value to return when the caller is in the same thread that /// first called `Pool::get`. /// /// This is set to None when a Pool is first created, and set to Some /// once the first thread calls Pool::get. owner_val: UnsafeCell<Option<T>>, } // SAFETY: Since we want to use a Pool from multiple threads simultaneously // behind an Arc, we need for it to be Sync. In cases where T is sync, // Pool<T> would be Sync. However, since we use a Pool to store mutable // scratch space, we wind up using a T that has interior mutability and is // thus itself not Sync. So what we *really* want is for our Pool<T> to by // Sync even when T is not Sync (but is at least Send). // // The only non-sync aspect of a Pool is its 'owner_val' field, which is // used to implement faster access to a pool value in the common case of // a pool being accessed in the same thread in which it was created. The // 'stack' field is also shared, but a Mutex<T> where T: Send is already // Sync. So we only need to worry about 'owner_val'. // // The key is to guarantee that 'owner_val' can only ever be accessed from // one thread. In our implementation below, we guarantee this by only // returning the 'owner_val' when the ID of the current thread matches the // ID of the thread that first called 'Pool::get'. Since this can only ever // be one thread, it follows that only one thread can access 'owner_val' at // any point in time. Thus, it is safe to declare that Pool<T> is Sync when // T is Send. // // If there is a way to achieve our performance goals using safe code, then // I would very much welcome a patch. As it stands, the implementation // below tries to balance safety with performance. The case where a Regex // is used from multiple threads simultaneously will suffer a bit since // getting a value out of the pool will require unlocking a mutex. // // We require `F: Send + Sync` because we call `F` at any point on demand, // potentially from multiple threads simultaneously. unsafe impl<T: Send, F: Send + Sync> Sync for Pool<T, F> {} // If T is UnwindSafe, then since we provide exclusive access to any // particular value in the pool, the pool should therefore also be // considered UnwindSafe. // // We require `F: UnwindSafe + RefUnwindSafe` because we call `F` at any // point on demand, so it needs to be unwind safe on both dimensions for // the entire Pool to be unwind safe. impl<T: UnwindSafe, F: UnwindSafe + RefUnwindSafe> UnwindSafe for Pool<T, F> {} // If T is UnwindSafe, then since we provide exclusive access to any // particular value in the pool, the pool should therefore also be // considered RefUnwindSafe. // // We require `F: UnwindSafe + RefUnwindSafe` because we call `F` at any // point on demand, so it needs to be unwind safe on both dimensions for // the entire Pool to be unwind safe. impl<T: UnwindSafe, F: UnwindSafe + RefUnwindSafe> RefUnwindSafe for Pool<T, F> { } impl<T, F> Pool<T, F> { /// Create a new pool. The given closure is used to create values in /// the pool when necessary. pub(super) fn new(create: F) -> Pool<T, F> { // MSRV(1.63): Mark this function as 'const'. I've arranged the // code such that it should "just work." Then mark the public // 'Pool::new' method as 'const' too. (The alloc-only Pool::new // is already 'const', so that should "just work" too.) The only // thing we're waiting for is Mutex::new to be const. let mut stacks = Vec::with_capacity(MAX_POOL_STACKS); for _ in 0..stacks.capacity() { stacks.push(CacheLine(Mutex::new(vec![]))); } let owner = AtomicUsize::new(THREAD_ID_UNOWNED); let owner_val = UnsafeCell::new(None); // init'd on first access Pool { create, stacks, owner, owner_val } } } impl<T: Send, F: Fn() -> T> Pool<T, F> { /// Get a value from the pool. This may block if another thread is also /// attempting to retrieve a value from the pool. #[inline] pub(super) fn get(&self) -> PoolGuard<'_, T, F> { // Our fast path checks if the caller is the thread that "owns" // this pool. Or stated differently, whether it is the first thread // that tried to extract a value from the pool. If it is, then we // can return a T to the caller without going through a mutex. // // SAFETY: We must guarantee that only one thread gets access // to this value. Since a thread is uniquely identified by the // THREAD_ID thread local, it follows that if the caller's thread // ID is equal to the owner, then only one thread may receive this // value. This is also why we can get away with what looks like a // racy load and a store. We know that if 'owner == caller', then // only one thread can be here, so we don't need to worry about any // other thread setting the owner to something else. let caller = THREAD_ID.with(|id| *id); let owner = self.owner.load(Ordering::Acquire); if caller == owner { // N.B. We could also do a CAS here instead of a load/store, // but ad hoc benchmarking suggests it is slower. And a lot // slower in the case where `get_slow` is common. self.owner.store(THREAD_ID_INUSE, Ordering::Release); return self.guard_owned(caller); } self.get_slow(caller, owner) } /// This is the "slow" version that goes through a mutex to pop an /// allocated value off a stack to return to the caller. (Or, if the /// stack is empty, a new value is created.) /// /// If the pool has no owner, then this will set the owner. #[cold] fn get_slow( &self, caller: usize, owner: usize, ) -> PoolGuard<'_, T, F> { if owner == THREAD_ID_UNOWNED { // This sentinel means this pool is not yet owned. We try to // atomically set the owner. If we do, then this thread becomes // the owner and we can return a guard that represents the // special T for the owner. // // Note that we set the owner to a different sentinel that // indicates that the owned value is in use. The owner ID will // get updated to the actual ID of this thread once the guard // returned by this function is put back into the pool. let res = self.owner.compare_exchange( THREAD_ID_UNOWNED, THREAD_ID_INUSE, Ordering::AcqRel, Ordering::Acquire, ); if res.is_ok() { // SAFETY: A successful CAS above implies this thread is // the owner and that this is the only such thread that // can reach here. Thus, there is no data race. unsafe { *self.owner_val.get() = Some((self.create)()); } return self.guard_owned(caller); } } let stack_id = caller % self.stacks.len(); // We try to acquire exclusive access to this thread's stack, and // if so, grab a value from it if we can. We put this in a loop so // that it's easy to tweak and experiment with a different number // of tries. In the end, I couldn't see anything obviously better // than one attempt in ad hoc testing. for _ in 0..1 { let mut stack = match self.stacks[stack_id].0.try_lock() { Err(_) => continue, Ok(stack) => stack, }; if let Some(value) = stack.pop() { return self.guard_stack(value); } // Unlock the mutex guarding the stack before creating a fresh // value since we no longer need the stack. drop(stack); let value = Box::new((self.create)()); return self.guard_stack(value); } // We're only here if we could get access to our stack, so just // create a new value. This seems like it could be wasteful, but // waiting for exclusive access to a stack when there's high // contention is brutal for perf. self.guard_stack_transient(Box::new((self.create)())) } /// Puts a value back into the pool. Callers don't need to call this. /// Once the guard that's returned by 'get' is dropped, it is put back /// into the pool automatically. #[inline] fn put_value(&self, value: Box<T>) { let caller = THREAD_ID.with(|id| *id); let stack_id = caller % self.stacks.len(); // As with trying to pop a value from this thread's stack, we // merely attempt to get access to push this value back on the // stack. If there's too much contention, we just give up and throw // the value away. // // Interestingly, in ad hoc benchmarking, it is beneficial to // attempt to push the value back more than once, unlike when // popping the value. I don't have a good theory for why this is. // I guess if we drop too many values then that winds up forcing // the pop operation to create new fresh values and thus leads to // less reuse. There's definitely a balancing act here. for _ in 0..10 { let mut stack = match self.stacks[stack_id].0.try_lock() { Err(_) => continue, Ok(stack) => stack, }; stack.push(value); return; } } /// Create a guard that represents the special owned T. #[inline] fn guard_owned(&self, caller: usize) -> PoolGuard<'_, T, F> { PoolGuard { pool: self, value: Err(caller), discard: false } } /// Create a guard that contains a value from the pool's stack. #[inline] fn guard_stack(&self, value: Box<T>) -> PoolGuard<'_, T, F> { PoolGuard { pool: self, value: Ok(value), discard: false } } /// Create a guard that contains a value from the pool's stack with an /// instruction to throw away the value instead of putting it back /// into the pool. #[inline] fn guard_stack_transient(&self, value: Box<T>) -> PoolGuard<'_, T, F> { PoolGuard { pool: self, value: Ok(value), discard: true } } } impl<T: core::fmt::Debug, F> core::fmt::Debug for Pool<T, F> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Pool") .field("stacks", &self.stacks) .field("owner", &self.owner) .field("owner_val", &self.owner_val) .finish() } } /// A guard that is returned when a caller requests a value from the pool. pub(super) struct PoolGuard<'a, T: Send, F: Fn() -> T> { /// The pool that this guard is attached to. pool: &'a Pool<T, F>, /// This is Err when the guard represents the special "owned" value. /// In which case, the value is retrieved from 'pool.owner_val'. And /// in the special case of `Err(THREAD_ID_DROPPED)`, it means the /// guard has been put back into the pool and should no longer be used. value: Result<Box<T>, usize>, /// When true, the value should be discarded instead of being pushed /// back into the pool. We tend to use this under high contention, and /// this allows us to avoid inflating the size of the pool. (Because /// under contention, we tend to create more values instead of waiting /// for access to a stack of existing values.) discard: bool, } impl<'a, T: Send, F: Fn() -> T> PoolGuard<'a, T, F> { /// Return the underlying value. #[inline] pub(super) fn value(&self) -> &T { match self.value { Ok(ref v) => &**v, // SAFETY: This is safe because the only way a PoolGuard gets // created for self.value=Err is when the current thread // corresponds to the owning thread, of which there can only // be one. Thus, we are guaranteed to be providing exclusive // access here which makes this safe. // // Also, since 'owner_val' is guaranteed to be initialized // before an owned PoolGuard is created, the unchecked unwrap // is safe. Err(id) => unsafe { // This assert is *not* necessary for safety, since we // should never be here if the guard had been put back into // the pool. This is a sanity check to make sure we didn't // break an internal invariant. debug_assert_ne!(THREAD_ID_DROPPED, id); (*self.pool.owner_val.get()).as_ref().unwrap_unchecked() }, } } /// Return the underlying value as a mutable borrow. #[inline] pub(super) fn value_mut(&mut self) -> &mut T { match self.value { Ok(ref mut v) => &mut **v, // SAFETY: This is safe because the only way a PoolGuard gets // created for self.value=None is when the current thread // corresponds to the owning thread, of which there can only // be one. Thus, we are guaranteed to be providing exclusive // access here which makes this safe. // // Also, since 'owner_val' is guaranteed to be initialized // before an owned PoolGuard is created, the unwrap_unchecked // is safe. Err(id) => unsafe { // This assert is *not* necessary for safety, since we // should never be here if the guard had been put back into // the pool. This is a sanity check to make sure we didn't // break an internal invariant. debug_assert_ne!(THREAD_ID_DROPPED, id); (*self.pool.owner_val.get()).as_mut().unwrap_unchecked() }, } } /// Consumes this guard and puts it back into the pool. #[inline] pub(super) fn put(this: PoolGuard<'_, T, F>) { // Since this is effectively consuming the guard and putting the // value back into the pool, there's no reason to run its Drop // impl after doing this. I don't believe there is a correctness // problem with doing so, but there's definitely a perf problem // by redoing this work. So we avoid it. let mut this = core::mem::ManuallyDrop::new(this); this.put_imp(); } /// Puts this guard back into the pool by only borrowing the guard as /// mutable. This should be called at most once. #[inline(always)] fn put_imp(&mut self) { match core::mem::replace(&mut self.value, Err(THREAD_ID_DROPPED)) { Ok(value) => { // If we were told to discard this value then don't bother // trying to put it back into the pool. This occurs when // the pop operation failed to acquire a lock and we // decided to create a new value in lieu of contending for // the lock. if self.discard { return; } self.pool.put_value(value); } // If this guard has a value "owned" by the thread, then // the Pool guarantees that this is the ONLY such guard. // Therefore, in order to place it back into the pool and make // it available, we need to change the owner back to the owning // thread's ID. But note that we use the ID that was stored in // the guard, since a guard can be moved to another thread and // dropped. (A previous iteration of this code read from the // THREAD_ID thread local, which uses the ID of the current // thread which may not be the ID of the owning thread! This // also avoids the TLS access, which is likely a hair faster.) Err(owner) => { // If we hit this point, it implies 'put_imp' has been // called multiple times for the same guard which in turn // corresponds to a bug in this implementation. assert_ne!(THREAD_ID_DROPPED, owner); self.pool.owner.store(owner, Ordering::Release); } } } } impl<'a, T: Send, F: Fn() -> T> Drop for PoolGuard<'a, T, F> { #[inline] fn drop(&mut self) { self.put_imp(); } } impl<'a, T: Send + core::fmt::Debug, F: Fn() -> T> core::fmt::Debug for PoolGuard<'a, T, F> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("PoolGuard") .field("pool", &self.pool) .field("value", &self.value) .finish() } } } #[cfg(test)] mod tests { use core::panic::{RefUnwindSafe, UnwindSafe}; use alloc::vec; use super::*; #[test] fn oibits() { fn assert_oitbits<T: Send + Sync + UnwindSafe + RefUnwindSafe>() {} assert_oitbits::<Pool<Vec<u32>>>(); assert_oitbits::<Pool<core::cell::RefCell<Vec<u32>>>>(); assert_oitbits::< Pool< Vec<u32>, Box< dyn Fn() -> Vec<u32> + Send + Sync + UnwindSafe + RefUnwindSafe, >, >, >(); } // Tests that Pool implements the "single owner" optimization. That is, the // thread that first accesses the pool gets its own copy, while all other // threads get distinct copies. #[cfg(feature = "std")] #[test] fn thread_owner_optimization() { use std::{cell::RefCell, sync::Arc, vec}; let pool: Arc<Pool<RefCell<Vec<char>>>> = Arc::new(Pool::new(|| RefCell::new(vec!['a']))); pool.get().borrow_mut().push('x'); let pool1 = pool.clone(); let t1 = std::thread::spawn(move || { let guard = pool1.get(); guard.borrow_mut().push('y'); }); let pool2 = pool.clone(); let t2 = std::thread::spawn(move || { let guard = pool2.get(); guard.borrow_mut().push('z'); }); t1.join().unwrap(); t2.join().unwrap(); // If we didn't implement the single owner optimization, then one of // the threads above is likely to have mutated the [a, x] vec that // we stuffed in the pool before spawning the threads. But since // neither thread was first to access the pool, and because of the // optimization, we should be guaranteed that neither thread mutates // the special owned pool value. // // (Technically this is an implementation detail and not a contract of // Pool's API.) assert_eq!(vec!['a', 'x'], *pool.get().borrow()); } // This tests that if the "owner" of a pool asks for two values, then it // gets two distinct values and not the same one. This test failed in the // course of developing the pool, which in turn resulted in UB because it // permitted getting aliasing &mut borrows to the same place in memory. #[test] fn thread_owner_distinct() { let pool = Pool::new(|| vec!['a']); { let mut g1 = pool.get(); let v1 = &mut *g1; let mut g2 = pool.get(); let v2 = &mut *g2; v1.push('b'); v2.push('c'); assert_eq!(&mut vec!['a', 'b'], v1); assert_eq!(&mut vec!['a', 'c'], v2); } // This isn't technically guaranteed, but we // expect to now get the "owned" value (the first // call to 'get()' above) now that it's back in // the pool. assert_eq!(&mut vec!['a', 'b'], &mut *pool.get()); } // This tests that we can share a guard with another thread, mutate the // underlying value and everything works. This failed in the course of // developing a pool since the pool permitted 'get()' to return the same // value to the owner thread, even before the previous value was put back // into the pool. This in turn resulted in this test producing a data race. #[cfg(feature = "std")] #[test] fn thread_owner_sync() { let pool = Pool::new(|| vec!['a']); { let mut g1 = pool.get(); let mut g2 = pool.get(); std::thread::scope(|s| { s.spawn(|| { g1.push('b'); }); s.spawn(|| { g2.push('c'); }); }); let v1 = &mut *g1; let v2 = &mut *g2; assert_eq!(&mut vec!['a', 'b'], v1); assert_eq!(&mut vec!['a', 'c'], v2); } // This isn't technically guaranteed, but we // expect to now get the "owned" value (the first // call to 'get()' above) now that it's back in // the pool. assert_eq!(&mut vec!['a', 'b'], &mut *pool.get()); } // This tests that if we move a PoolGuard that is owned by the current // thread to another thread and drop it, then the thread owner doesn't // change. During development of the pool, this test failed because the // PoolGuard assumed it was dropped in the same thread from which it was // created, and thus used the current thread's ID as the owner, which could // be different than the actual owner of the pool. #[cfg(feature = "std")] #[test] fn thread_owner_send_drop() { let pool = Pool::new(|| vec!['a']); // Establishes this thread as the owner. { pool.get().push('b'); } std::thread::scope(|s| { // Sanity check that we get the same value back. // (Not technically guaranteed.) let mut g = pool.get(); assert_eq!(&vec!['a', 'b'], &*g); // Now push it to another thread and drop it. s.spawn(move || { g.push('c'); }) .join() .unwrap(); }); // Now check that we're still the owner. This is not technically // guaranteed by the API, but is true in practice given the thread // owner optimization. assert_eq!(&vec!['a', 'b', 'c'], &*pool.get()); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������