#![allow(clippy::transmute_ptr_to_ref)]
use onig_sys;
use std::iter::FusedIterator;
use std::mem::transmute;
use std::os::raw::{c_int, c_void};
use std::ptr::null_mut;
use super::flags::TraverseCallbackAt;
use super::CaptureTreeNode;
#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct Region {
pub(crate) raw: onig_sys::OnigRegion,
}
impl Region {
pub fn new() -> Region {
Region {
raw: onig_sys::OnigRegion {
allocated: 0,
num_regs: 0,
beg: null_mut(),
end: null_mut(),
history_root: null_mut(),
},
}
}
pub fn with_capacity(capacity: usize) -> Region {
let mut region = Self::new();
region.reserve(capacity);
region
}
pub unsafe fn clone_from_raw(ptr: *mut onig_sys::OnigRegion) -> Self {
let mut region = Self::new();
onig_sys::onig_region_copy(&mut region.raw, ptr);
region
}
pub fn clear(&mut self) {
unsafe {
onig_sys::onig_region_clear(&mut self.raw);
}
}
pub fn capacity(&self) -> usize {
self.raw.allocated as usize
}
pub fn reserve(&mut self, new_capacity: usize) {
let r = unsafe { onig_sys::onig_region_resize(&mut self.raw, new_capacity as c_int) };
if r != onig_sys::ONIG_NORMAL as i32 {
panic!("Onig: fail to memory allocation during region resize")
}
}
pub fn len(&self) -> usize {
self.raw.num_regs as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn pos(&self, pos: usize) -> Option<(usize, usize)> {
if pos >= self.len() {
return None;
}
let pos = pos as isize;
let (beg, end) = unsafe { (*self.raw.beg.offset(pos), *self.raw.end.offset(pos)) };
if beg != onig_sys::ONIG_REGION_NOTPOS {
Some((beg as usize, end as usize))
} else {
None
}
}
pub fn tree(&self) -> Option<&CaptureTreeNode> {
let tree = unsafe { onig_sys::onig_get_capture_tree(self.raw_mut()) };
if tree.is_null() {
None
} else {
Some(unsafe { transmute(tree) })
}
}
pub fn iter(&self) -> RegionIter<'_> {
RegionIter {
region: self,
pos: 0,
}
}
pub fn tree_traverse<F>(&self, callback: F) -> i32
where
F: Fn(u32, (usize, usize), u32) -> bool,
{
self.tree_traverse_at(TraverseCallbackAt::CALLBACK_AT_FIRST, callback)
}
pub fn tree_traverse_at<F>(&self, at: TraverseCallbackAt, mut callback: F) -> i32
where
F: Fn(u32, (usize, usize), u32) -> bool,
{
use onig_sys::onig_capture_tree_traverse;
extern "C" fn traverse_cb<F>(
group: c_int,
beg: c_int,
end: c_int,
level: c_int,
_at: c_int,
ud: *mut c_void,
) -> c_int
where
F: Fn(u32, (usize, usize), u32) -> bool,
{
let callback = unsafe { &*(ud as *mut F) };
if callback(group as u32, (beg as usize, end as usize), level as u32) {
0
} else {
-1
}
}
unsafe {
onig_capture_tree_traverse(
self.raw_mut(),
at.bits() as c_int,
Some(traverse_cb::<F>),
&mut callback as *mut F as *mut c_void,
)
}
}
fn raw_mut(&self) -> *mut onig_sys::OnigRegion {
&self.raw as *const onig_sys::OnigRegion as *mut onig_sys::OnigRegion
}
}
impl Default for Region {
fn default() -> Self {
Region::new()
}
}
impl Drop for Region {
fn drop(&mut self) {
unsafe {
onig_sys::onig_region_free(&mut self.raw, 0);
}
}
}
impl Clone for Region {
fn clone(&self) -> Self {
unsafe { Self::clone_from_raw(self.raw_mut()) }
}
}
impl<'a> IntoIterator for &'a Region {
type Item = (usize, usize);
type IntoIter = RegionIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct RegionIter<'a> {
region: &'a Region,
pos: usize,
}
impl<'a> Iterator for RegionIter<'a> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let next = self.region.pos(self.pos);
self.pos += 1;
next
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.region.len();
(len, Some(len))
}
fn count(self) -> usize {
self.region.len()
}
}
impl<'a> FusedIterator for RegionIter<'a> {}
impl<'a> ExactSizeIterator for RegionIter<'a> {}
#[cfg(test)]
mod tests {
use super::super::{Regex, SearchOptions};
use super::*;
#[test]
fn test_region_create() {
Region::new();
}
#[test]
fn test_region_clear() {
let mut region = Region::new();
region.clear();
}
#[test]
fn test_region_copy() {
let region = Region::new();
let new_region = region.clone();
assert_eq!(new_region.len(), region.len());
}
#[test]
fn test_region_resize() {
{
let mut region = Region::new();
assert!(region.capacity() == 0);
region.reserve(100);
{
let region_borrowed = ®ion;
assert!(region_borrowed.capacity() == 100);
}
}
{
let region = Region::with_capacity(10);
assert!(region.capacity() == 10);
}
}
#[test]
fn test_region_empty_iterate() {
let region = Region::new();
for _ in ®ion {
panic!("region should not contain any elements");
}
}
#[test]
fn test_region_iter_returns_iterator() {
let region = Region::new();
let all = region.iter().collect::<Vec<_>>();
assert_eq!(all, Vec::new());
}
#[test]
fn test_region_iterate_with_captures() {
let mut region = Region::new();
let reg = Regex::new("(a+)(b+)(c+)").unwrap();
let res = reg.search_with_options(
"aaaabbbbc",
0,
9,
SearchOptions::SEARCH_OPTION_NONE,
Some(&mut region),
);
assert!(res.is_some());
let all = region.iter().collect::<Vec<_>>();
assert_eq!(all, vec![(0, 9), (0, 4), (4, 8), (8, 9)]);
}
#[test]
fn test_region_all_iteration_options() {
let mut region = Region::new();
let reg = Regex::new("a(b)").unwrap();
let res = reg.search_with_options(
"habitat",
0,
7,
SearchOptions::SEARCH_OPTION_NONE,
Some(&mut region),
);
assert!(res.is_some());
let mut a = Vec::<(usize, usize)>::new();
for pos in ®ion {
a.push(pos)
}
let b = region.iter().collect::<Vec<_>>();
let expected = vec![(1, 3), (2, 3)];
assert_eq!(expected, a);
assert_eq!(expected, b);
assert_eq!(2, region.iter().count());
}
}