use std::ptr::NonNull;
use crate::Answer;
use crate::error::{self, Error};
use crate::sink;
use crate::sys;
const PLANE: &str = "codex";
#[repr(C)]
struct Handle {
_opaque: [u8; 0],
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Encoding {
#[default]
AdoptMin,
PlainOnly,
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Options {
struct_size: u32,
sample_rate: u32,
encoding: u32,
reserved: u32,
}
impl Default for Options {
fn default() -> Self {
Self {
struct_size: size_of::<Self>() as u32,
sample_rate: 0,
encoding: 0,
reserved: 0,
}
}
}
impl Options {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn sample_rate(mut self, rate: u32) -> Self {
self.sample_rate = rate;
self
}
#[must_use]
pub fn without_locating(mut self) -> Self {
self.sample_rate = u32::MAX;
self
}
#[must_use]
pub fn encoding(mut self, encoding: Encoding) -> Self {
self.encoding = match encoding {
Encoding::AdoptMin => 0,
Encoding::PlainOnly => 1,
};
self
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Stats {
struct_size: u32,
sample_rate: u32,
locates: u32,
reserved: u32,
text_len: usize,
index_bytes: usize,
tree_bytes: usize,
locate_bytes: usize,
}
impl Default for Stats {
fn default() -> Self {
Self {
struct_size: size_of::<Self>() as u32,
sample_rate: 0,
locates: 0,
reserved: 0,
text_len: 0,
index_bytes: 0,
tree_bytes: 0,
locate_bytes: 0,
}
}
}
impl Stats {
#[must_use]
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[must_use]
pub fn locates(&self) -> bool {
self.locates != 0
}
#[must_use]
pub fn text_len(&self) -> usize {
self.text_len
}
#[must_use]
pub fn index_bytes(&self) -> usize {
self.index_bytes
}
#[must_use]
pub fn tree_bytes(&self) -> usize {
self.tree_bytes
}
#[must_use]
pub fn locate_bytes(&self) -> usize {
self.locate_bytes
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Rows {
lo: usize,
hi: usize,
}
impl Rows {
#[must_use]
pub fn low(&self) -> usize {
self.lo
}
#[must_use]
pub fn high(&self) -> usize {
self.hi
}
#[must_use]
pub fn len(&self) -> usize {
self.hi.saturating_sub(self.lo)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct Codex {
handle: NonNull<Handle>,
}
impl Codex {
#[must_use]
pub fn max_text_len() -> usize {
unsafe { ffi::irgx_codex_max_text_len() }
}
pub fn build(text: &[u8]) -> Result<Self, Error> {
Self::build_with(text, &Options::default())
}
pub fn build_with(text: &[u8], options: &Options) -> Result<Self, Error> {
Self::opened(|out| unsafe {
ffi::irgx_codex_build(text.as_ptr(), text.len(), options, out)
})
}
pub fn load(bytes: &[u8]) -> Result<Self, Error> {
Self::opened(|out| unsafe { ffi::irgx_codex_load(bytes.as_ptr(), bytes.len(), out) })
}
fn opened(open: impl FnOnce(*mut *mut Handle) -> i32) -> Result<Self, Error> {
let mut out: *mut Handle = std::ptr::null_mut();
let status = open(&raw mut out);
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
NonNull::new(out)
.map(|handle| Self { handle })
.ok_or_else(|| Error::Inconsistent {
message: "the codex plane reported success and produced no handle".to_owned(),
})
}
#[must_use]
pub fn len(&self) -> usize {
unsafe { ffi::irgx_codex_len(self.handle.as_ptr()) }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn measure(&self) -> Result<Stats, Error> {
let mut out = Stats::default();
let status = unsafe { ffi::irgx_codex_measure(self.handle.as_ptr(), &raw mut out) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(out)
}
pub fn count(&self, pattern: &[u8]) -> Result<usize, Error> {
let mut out = 0usize;
let status = unsafe {
ffi::irgx_codex_count(
self.handle.as_ptr(),
pattern.as_ptr(),
pattern.len(),
&raw mut out,
)
};
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(out)
}
pub fn locate(&self, pattern: &[u8]) -> Result<Answer<Vec<usize>>, Error> {
let hint = self.count(pattern)?;
sink::reap(PLANE, hint, |out, cap, written| {
unsafe {
ffi::irgx_codex_locate(
self.handle.as_ptr(),
pattern.as_ptr(),
pattern.len(),
out,
cap,
written,
)
}
})
}
pub fn position(&self, row: usize) -> Result<Answer<usize>, Error> {
let mut out = 0usize;
let status = unsafe { ffi::irgx_codex_position(self.handle.as_ptr(), row, &raw mut out) };
if status == sys::STALE {
return Ok(Answer::Declined);
}
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(Answer::Given(out))
}
pub fn rows(&self) -> Result<Rows, Error> {
let mut out = Rows::default();
let status = unsafe { ffi::irgx_codex_rows_whole(self.handle.as_ptr(), &raw mut out) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(out)
}
pub fn narrow(&self, rows: &mut Rows, byte: u8) -> Result<bool, Error> {
let status = unsafe { ffi::irgx_codex_rows_extend(self.handle.as_ptr(), rows, byte) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(status == sys::MATCH)
}
pub fn extract(&self, at: usize) -> Result<Vec<u8>, Error> {
let hint = self.len().saturating_sub(at);
sink::reap_all(PLANE, hint, |out, cap, written| {
unsafe { ffi::irgx_codex_extract(self.handle.as_ptr(), at, out, cap, written) }
})
}
pub fn save(&self) -> Result<Vec<u8>, Error> {
sink::reap_all(PLANE, 0, |out, cap, written| {
unsafe { ffi::irgx_codex_save(self.handle.as_ptr(), out, cap, written) }
})
}
}
impl Drop for Codex {
fn drop(&mut self) {
unsafe { ffi::irgx_codex_free(self.handle.as_ptr()) };
}
}
impl std::fmt::Debug for Codex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Codex")
.field("text_len", &self.len())
.field("stats", &self.measure().ok())
.finish()
}
}
mod ffi {
use super::{Handle, Options, Rows, Stats};
unsafe extern "C" {
pub fn irgx_codex_max_text_len() -> usize;
pub fn irgx_codex_build(
text: *const u8,
len: usize,
opts: *const Options,
out: *mut *mut Handle,
) -> i32;
pub fn irgx_codex_load(bytes: *const u8, len: usize, out: *mut *mut Handle) -> i32;
pub fn irgx_codex_free(cx: *mut Handle);
pub fn irgx_codex_len(cx: *const Handle) -> usize;
pub fn irgx_codex_measure(cx: *const Handle, out: *mut Stats) -> i32;
pub fn irgx_codex_count(
cx: *const Handle,
pattern: *const u8,
len: usize,
out: *mut usize,
) -> i32;
pub fn irgx_codex_locate(
cx: *const Handle,
pattern: *const u8,
len: usize,
out: *mut usize,
cap: usize,
written: *mut usize,
) -> i32;
pub fn irgx_codex_position(cx: *const Handle, row: usize, out: *mut usize) -> i32;
pub fn irgx_codex_rows_whole(cx: *const Handle, out: *mut Rows) -> i32;
pub fn irgx_codex_rows_extend(cx: *const Handle, rows: *mut Rows, byte: u8) -> i32;
pub fn irgx_codex_extract(
cx: *mut Handle,
at: usize,
out: *mut u8,
cap: usize,
written: *mut usize,
) -> i32;
pub fn irgx_codex_save(
cx: *mut Handle,
out: *mut u8,
cap: usize,
written: *mut usize,
) -> i32;
}
}