use std::{
collections::HashMap,
sync::{
atomic::{AtomicU64, Ordering},
Arc, Mutex, OnceLock,
},
};
use crate::Matcher;
pub type SharedMatcher = Arc<Mutex<Matcher>>;
struct Entry {
matcher: SharedMatcher,
holds: usize,
}
fn table() -> &'static Mutex<HashMap<u64, Entry>> {
static TABLE: OnceLock<Mutex<HashMap<u64, Entry>>> = OnceLock::new();
TABLE.get_or_init(Mutex::default)
}
pub fn register(matcher: Matcher) -> (u64, SharedMatcher) {
static NEXT: AtomicU64 = AtomicU64::new(1);
let handle = NEXT.fetch_add(1, Ordering::Relaxed);
let shared = Arc::new(Mutex::new(matcher));
table().lock().expect("grammar table lock").insert(
handle,
Entry {
matcher: shared.clone(),
holds: 1,
},
);
(handle, shared)
}
fn matcher(handle: u64) -> Option<SharedMatcher> {
table()
.lock()
.expect("grammar table lock")
.get(&handle)
.map(|entry| entry.matcher.clone())
}
fn with<R>(handle: u64, failed: R, run: impl FnOnce(&mut Matcher) -> R) -> R {
match matcher(handle) {
Some(shared) => run(&mut shared.lock().expect("matcher lock")),
None => failed,
}
}
extern "C" fn ts_grammar_is_terminated(handle: u64) -> i32 {
with(handle, -1, |m| i32::from(m.is_terminated()))
}
extern "C" fn ts_grammar_accept_token(handle: u64, token: i32) -> i32 {
with(handle, -1, |m| i32::from(m.accept_token(token)))
}
extern "C" fn ts_grammar_rollback(handle: u64, num_tokens: i32) -> i32 {
with(handle, -1, |m| {
if m.rollback(num_tokens).is_ok() {
0
} else {
-1
}
})
}
extern "C" fn ts_grammar_fill_bitmask(
handle: u64,
data: usize,
rows: i64,
width: i64,
row: i32,
) -> i32 {
with(handle, -1, |m| {
match unsafe { m.fill_bitmask(data, rows, width, row) } {
Ok(()) => 0,
Err(_) => -1,
}
})
}
extern "C" fn ts_grammar_traverse_draft_tree(
handle: u64,
next_node: usize,
next_sibling: usize,
draft_tokens: usize,
num_nodes: i64,
bitmask: usize,
width: i64,
) -> i32 {
let Ok(nodes) = usize::try_from(num_nodes) else {
return -1;
};
let column =
|address: usize| unsafe { std::slice::from_raw_parts(address as *const i64, nodes) };
with(handle, -1, |m| {
match unsafe {
m.traverse_draft_tree(
column(next_node),
column(next_sibling),
column(draft_tokens),
bitmask,
width,
)
} {
Ok(()) => 0,
Err(_) => -1,
}
})
}
extern "C" fn ts_grammar_retain(handle: u64) -> i32 {
match table().lock().expect("grammar table lock").get_mut(&handle) {
Some(entry) => {
entry.holds += 1;
0
}
None => -1,
}
}
extern "C" fn ts_grammar_free(handle: u64) {
let mut table = table().lock().expect("grammar table lock");
if let Some(entry) = table.get_mut(&handle) {
entry.holds -= 1;
if entry.holds == 0 {
table.remove(&handle);
}
}
}
pub fn entries() -> Vec<(&'static str, usize)> {
vec![
(
"is_terminated",
ts_grammar_is_terminated as *const () as usize,
),
(
"accept_token",
ts_grammar_accept_token as *const () as usize,
),
("rollback", ts_grammar_rollback as *const () as usize),
(
"fill_bitmask",
ts_grammar_fill_bitmask as *const () as usize,
),
(
"traverse_draft_tree",
ts_grammar_traverse_draft_tree as *const () as usize,
),
("retain", ts_grammar_retain as *const () as usize),
("free", ts_grammar_free as *const () as usize),
]
}