use std::marker::PhantomData;
use std::ptr::NonNull;
use crate::error::{self, Error};
use crate::sink;
use crate::sys;
use crate::{Answer, Regex};
const PLANE: &str = "literals";
#[repr(C)]
struct Handle {
_opaque: [u8; 0],
}
const UNBOUNDED: u32 = u32::MAX;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Place {
Required,
Prefix,
Suffix,
Whole,
}
impl Place {
pub const ALL: [Self; 4] = [Self::Required, Self::Prefix, Self::Suffix, Self::Whole];
const fn ordinal(self) -> usize {
match self {
Self::Required => 0,
Self::Prefix => 1,
Self::Suffix => 2,
Self::Whole => 3,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Verdict {
#[default]
Nothing,
Candidate,
Exact,
}
impl Verdict {
#[must_use]
pub fn eliminates(self) -> bool {
self >= Self::Candidate
}
fn from_abi(raw: u32) -> Self {
match raw {
1 => Self::Candidate,
2 => Self::Exact,
_ => Self::Nothing,
}
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Promise {
struct_size: u32,
verdict: [u32; 4],
count: [u32; 4],
anchored: u32,
nullable: u32,
min_len: u32,
max_len: u32,
first_bytes: [u64; 4],
signature: [u64; 2],
}
impl Default for Promise {
fn default() -> Self {
Self {
struct_size: size_of::<Self>() as u32,
verdict: [0; 4],
count: [0; 4],
anchored: 0,
nullable: 0,
min_len: 0,
max_len: 0,
first_bytes: [0; 4],
signature: [0; 2],
}
}
}
impl Promise {
#[must_use]
pub fn verdict(&self, place: Place) -> Verdict {
Verdict::from_abi(self.verdict[place.ordinal()])
}
#[must_use]
pub fn count(&self, place: Place) -> usize {
self.count[place.ordinal()] as usize
}
#[must_use]
pub fn best(&self) -> Option<(Place, Verdict)> {
Place::ALL
.into_iter()
.map(|place| (place, self.verdict(place)))
.filter(|(_, verdict)| verdict.eliminates())
.max_by_key(|&(place, verdict)| (verdict, std::cmp::Reverse(self.count(place))))
}
#[must_use]
pub fn is_anchored(&self) -> bool {
self.anchored != 0
}
#[must_use]
pub fn is_nullable(&self) -> bool {
self.nullable != 0
}
#[must_use]
pub fn min_len(&self) -> usize {
self.min_len as usize
}
#[must_use]
pub fn max_len(&self) -> Option<usize> {
(self.max_len != UNBOUNDED).then_some(self.max_len as usize)
}
#[must_use]
pub fn may_start_with(&self, byte: u8) -> bool {
!self.first_bytes_known()
|| self.first_bytes[usize::from(byte >> 6)] >> (byte & 63) & 1 == 1
}
#[must_use]
pub fn first_bytes_known(&self) -> bool {
self.first_bytes.iter().any(|word| *word != 0)
}
#[must_use]
pub fn signature(&self) -> u128 {
u128::from(self.signature[0]) | u128::from(self.signature[1]) << 64
}
}
pub struct Literals {
handle: NonNull<Handle>,
}
impl Literals {
pub fn open(re: &Regex) -> Result<Answer<Self>, Error> {
let mut out: *mut Handle = std::ptr::null_mut();
let status = re.with_handle(|raw| unsafe { ffi::irgx_literals_open(raw, &raw mut out) })?;
if status == sys::STALE {
return Ok(Answer::Declined);
}
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
NonNull::new(out)
.map(|handle| Answer::Given(Self { handle }))
.ok_or(Error::Inconsistent {
message: "the literal plane reported success and produced no handle".to_owned(),
})
}
pub fn promise(&self) -> Result<Promise, Error> {
let mut out = Promise::default();
let status = unsafe { ffi::irgx_literals_promise(self.handle.as_ptr(), &raw mut out) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(out)
}
pub fn set(&self, place: Place) -> Result<LiteralSet<'_>, Error> {
let mut verdict = 0u32;
let hint = self.promise().map(|p| p.count(place)).unwrap_or_default();
let rows = sink::reap_all(PLANE, hint, |out, cap, written| {
unsafe {
ffi::irgx_literals_set(
self.handle.as_ptr(),
place.ordinal() as u32,
&raw mut verdict,
out,
cap,
written,
)
}
})?;
Ok(LiteralSet {
verdict: Verdict::from_abi(verdict),
rows,
owner: PhantomData,
})
}
}
impl Drop for Literals {
fn drop(&mut self) {
unsafe { ffi::irgx_literals_free(self.handle.as_ptr()) };
}
}
pub struct LiteralSet<'a> {
verdict: Verdict,
rows: Vec<sys::Text>,
owner: PhantomData<&'a Literals>,
}
impl<'a> LiteralSet<'a> {
#[must_use]
pub fn verdict(&self) -> Verdict {
self.verdict
}
#[must_use]
pub fn len(&self) -> usize {
self.rows.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = &'a [u8]> + '_ {
self.rows.iter().map(|row| unsafe { sys::borrowed(row) })
}
}
impl<'a> IntoIterator for &'a LiteralSet<'a> {
type Item = &'a [u8];
type IntoIter = std::vec::IntoIter<&'a [u8]>;
fn into_iter(self) -> Self::IntoIter {
self.iter().collect::<Vec<_>>().into_iter()
}
}
impl std::fmt::Debug for LiteralSet<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LiteralSet")
.field("verdict", &self.verdict)
.field(
"members",
&self.iter().map(String::from_utf8_lossy).collect::<Vec<_>>(),
)
.finish()
}
}
mod ffi {
use super::{Handle, Promise, sys};
unsafe extern "C" {
pub fn irgx_literals_open(re: *mut sys::Regex, out: *mut *mut Handle) -> i32;
pub fn irgx_literals_free(lits: *mut Handle);
pub fn irgx_literals_promise(lits: *const Handle, out: *mut Promise) -> i32;
pub fn irgx_literals_set(
lits: *const Handle,
place: u32,
verdict: *mut u32,
out: *mut sys::Text,
cap: usize,
written: *mut usize,
) -> i32;
}
}