use std::cmp;
use std::ptr;
use std::slice;
use libc::c_void;
use pcre2_sys::*;
use crate::error::Error;
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 {
panic!("BUG: {}", Error::jit(error_code));
}
rc == 1
}
pub fn version() -> (u32, u32) {
(PCRE2_MAJOR, PCRE2_MINOR)
}
pub struct Code {
code: *mut pcre2_code_8,
compiled_jit: bool,
#[allow(dead_code)]
ctx: CompileContext,
}
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 {
pub 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 })
}
}
pub 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))
}
}
pub fn capture_names(&self) -> Result<Vec<Option<String>>, Error> {
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)
.expect("valid UTF-8 for capture name");
}
Ok(names)
}
pub fn as_ptr(&self) -> *const pcre2_code_8 {
self.code
}
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)
}
}
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)
}
}
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)
}
}
pub 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)
}
}
}
pub struct CompileContext(*mut pcre2_compile_context_8);
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 {
pub 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)
}
pub 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
}
}
#[derive(Clone, Debug)]
pub struct MatchConfig {
pub max_jit_stack_size: Option<usize>,
}
impl Default for MatchConfig {
fn default() -> MatchConfig {
MatchConfig {
max_jit_stack_size: None,
}
}
}
pub 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,
}
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 {
pub 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,
}
}
pub fn config(&self) -> &MatchConfig {
&self.config
}
pub unsafe fn find(
&mut self,
code: &Code,
mut subject: &[u8],
start: usize,
options: u32,
) -> Result<bool, Error> {
const EMPTY: &[u8] = &[];
if subject.is_empty() {
subject = EMPTY;
}
let rc = pcre2_match_8(
code.as_ptr(),
subject.as_ptr(),
subject.len(),
start,
options,
self.as_mut_ptr(),
self.match_context,
);
if rc == PCRE2_ERROR_NOMATCH {
Ok(false)
} else if rc > 0 {
Ok(true)
} else {
assert!(rc != 0, "ovector should never be too small");
Err(Error::matching(rc))
}
}
fn as_mut_ptr(&mut self) -> *mut pcre2_match_data_8 {
self.match_data
}
pub fn ovector(&self) -> &[usize] {
unsafe {
slice::from_raw_parts(
self.ovector_ptr,
self.ovector_count as usize * 2,
)
}
}
}