use crate::cell::{TAG_ATOM, TAG_INT, Word, tag_of};
use crate::copyterm::restore_cells;
use crate::machine::{ContFn, Machine};
use crate::unify::unify;
const TBL: usize = 0; const NROWS: usize = 1;
const ARITY: usize = 2;
const IDX: usize = 3; const BLOB: usize = 4; const BLOBLEN: usize = 5; const CURSOR: usize = 6; const END: usize = 7; const RETRY: usize = 8; const KFN: usize = 9;
const KENV: usize = 10;
const QBAR: usize = 11;
const ARGS: usize = 12;
unsafe fn read_contfn(word: u64) -> ContFn {
unsafe { std::mem::transmute::<usize, ContFn>(word as usize) }
}
unsafe fn rodata_slice<'a>(ptr: u64, len: usize) -> &'a [Word] {
if ptr == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr as usize as *const Word, len) }
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn plg_rt_fact_first(
m: *mut Machine,
table_ptr: i64,
idx_ptr: i64,
blob_ptr: i64,
blob_len: i64,
nrows: i64,
arity: i64,
retry_ptr: i64,
) -> i32 {
let m = unsafe { &mut *m };
let nrows = nrows as usize;
let arity = arity as usize;
let (idx_for_frame, cursor0, end) = if arity == 0 {
(0u64, 0usize, nrows)
} else {
let a0 = m.deref(m.areg[0]);
if idx_ptr != 0 && matches!(tag_of(a0), TAG_ATOM | TAG_INT) {
let table = unsafe { rodata_slice(table_ptr as u64, nrows * arity) };
let idx = unsafe { rodata_slice(idx_ptr as u64, nrows) };
let (lo, hi) = equal_range(idx, table, arity, a0);
(idx_ptr as u64, lo, hi)
} else {
(0u64, 0usize, nrows)
}
};
let frame = m.frame_alloc(ARGS + arity);
m.heap[frame + TBL] = table_ptr as u64;
m.heap[frame + NROWS] = nrows as u64;
m.heap[frame + ARITY] = arity as u64;
m.heap[frame + IDX] = idx_for_frame;
m.heap[frame + BLOB] = blob_ptr as u64;
m.heap[frame + BLOBLEN] = blob_len as u64;
m.heap[frame + CURSOR] = cursor0 as u64;
m.heap[frame + END] = end as u64;
m.heap[frame + RETRY] = retry_ptr as u64;
m.heap[frame + KFN] = m.k_fn as usize as u64;
m.heap[frame + KENV] = m.k_env;
m.heap[frame + QBAR] = m.qbarrier as u64;
for c in 0..arity {
m.heap[frame + ARGS + c] = m.areg[c];
}
fact_scan(m, frame)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn plg_rt_fact_next(m: *mut Machine, frame: u64) -> i32 {
let m = unsafe { &mut *m };
let frame = frame as usize;
m.k_fn = unsafe { read_contfn(m.heap[frame + KFN]) };
m.k_env = m.heap[frame + KENV];
m.qbarrier = m.heap[frame + QBAR] as usize;
fact_scan(m, frame)
}
fn equal_range(idx: &[Word], table: &[Word], arity: usize, key: Word) -> (usize, usize) {
let col0 = |k: usize| table[idx[k] as usize * arity];
let (mut lo, mut hi) = (0usize, idx.len());
while lo < hi {
let mid = lo + (hi - lo) / 2;
if col0(mid) < key {
lo = mid + 1;
} else {
hi = mid;
}
}
let lower = lo;
hi = idx.len();
let mut lo2 = lower;
while lo2 < hi {
let mid = lo2 + (hi - lo2) / 2;
if col0(mid) <= key {
lo2 = mid + 1;
} else {
hi = mid;
}
}
(lower, lo2)
}
fn fact_scan(m: &mut Machine, frame: usize) -> i32 {
let nrows = m.heap[frame + NROWS] as usize;
let arity = m.heap[frame + ARITY] as usize;
let idx_ptr = m.heap[frame + IDX];
let end = m.heap[frame + END] as usize;
let retry = unsafe { read_contfn(m.heap[frame + RETRY]) };
let table = unsafe { rodata_slice(m.heap[frame + TBL], nrows * arity) };
let idx: Option<&[Word]> = (idx_ptr != 0).then(|| unsafe { rodata_slice(idx_ptr, nrows) });
let blob = unsafe { rodata_slice(m.heap[frame + BLOB], m.heap[frame + BLOBLEN] as usize) };
let clean_t = m.trail.len();
let clean_h = m.heap.len();
loop {
let pos = m.heap[frame + CURSOR] as usize;
if pos >= end {
return 0;
}
let row_idx = match idx {
Some(ix) => ix[pos] as usize,
None => pos,
};
let mut matched = true;
for c in 0..arity {
let col = table[row_idx * arity + c];
let w = match tag_of(col) {
TAG_ATOM | TAG_INT => col,
_ => restore_cells(m, blob, col),
};
let a = m.heap[frame + ARGS + c];
if !unify(m, a, w) {
matched = false;
break;
}
}
m.heap[frame + CURSOR] = (pos + 1) as u64;
if matched {
if pos + 1 < end {
m.push_cp_at(retry, frame as u64, clean_t, clean_h);
}
return 1;
}
m.rewind_to(clean_t, clean_h);
}
}