Skip to content

Commit

Permalink
Add Rust LuaJIT FFI library
Browse files Browse the repository at this point in the history
  • Loading branch information
jkl1337 committed Mar 7, 2024
1 parent 8328fe6 commit 62bfd88
Show file tree
Hide file tree
Showing 8 changed files with 812 additions and 0 deletions.
1 change: 1 addition & 0 deletions rjkiwi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
82 changes: 82 additions & 0 deletions rjkiwi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions rjkiwi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "rjkiwi"
version = "0.1.1"
edition = "2021"

[lib]
crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
casuarius = "0.1.1"

[target.'cfg(not(target_os = "linux"))'.dependencies]
mimalloc = "0.1"

[target.'cfg(target_os = "linux")'.dependencies]
mimalloc = { version = "0.1", features = ["local_dynamic_tls"] }
162 changes: 162 additions & 0 deletions rjkiwi/src/constraint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::{
f64::NAN,
ffi::{c_double, c_int, c_uint, c_void},
ptr::{self, drop_in_place},
};

use casuarius::{Expression, RelationalOperator, Term};

use crate::expr::{KiwiExpression, KiwiExpressionPtr};
use crate::var::SolverVariable;
use crate::*;

#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum KiwiRelOp {
LE,
GE,
EQ,
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_type_info(sz_align: *mut c_uint) {
*sz_align.offset(0) = std::mem::size_of::<KiwiConstraint>() as c_uint;
*sz_align.offset(1) = std::mem::align_of::<KiwiConstraint>() as c_uint;
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_init(
c: *mut KiwiConstraint,
lhs: *const KiwiExpressionPtr,
rhs: *const KiwiExpressionPtr,
op: KiwiRelOp,
strength: f64,
) {
let term_count = (if !lhs.is_null() {
(*lhs).term_count.max(0)
} else {
0
} + if !rhs.is_null() {
(*rhs).term_count.max(0)
} else {
0
}) as usize;

let mut terms = Vec::<Term<SolverVariable>>::new();
terms.reserve(term_count);

let mut constant = 0.0;

if !lhs.is_null() {
let lhs =
&*(core::slice::from_raw_parts(lhs as *const (), (*lhs).term_count.max(0) as usize)
as *const [()] as *const KiwiExpression);

terms.extend(lhs.terms.iter().filter_map(|t| match as_solver_var(t.var) {
Some(var) => Some(Term::new(var, t.coefficient)),
None => None,
}));
constant += lhs.constant;
}
if !rhs.is_null() {
let rhs =
&*(core::slice::from_raw_parts(rhs as *const (), (*rhs).term_count.max(0) as usize)
as *const [()] as *const KiwiExpression);

terms.extend(rhs.terms.iter().filter_map(|t| match as_solver_var(t.var) {
Some(var) => Some(Term::new(var, -t.coefficient)),
None => None,
}));
constant -= rhs.constant;
}

ptr::write(
c,
Constraint::new(
Expression::new(terms, constant),
match op {
KiwiRelOp::LE => RelationalOperator::LessOrEqual,
KiwiRelOp::GE => RelationalOperator::GreaterOrEqual,
KiwiRelOp::EQ => RelationalOperator::Equal,
},
strength,
),
);
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_destroy(c: *mut KiwiConstraint) {
if let Some(c) = not_null_mut(c) {
drop_in_place(c);
}
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_strength(c: *const KiwiConstraint) -> c_double {
match not_null_ref(c) {
Some(c) => c.strength(),
None => NAN,
}
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_op(c: *const KiwiConstraint) -> KiwiRelOp {
match not_null_ref(c) {
Some(c) => match c.op() {
RelationalOperator::LessOrEqual => KiwiRelOp::LE,
RelationalOperator::GreaterOrEqual => KiwiRelOp::GE,
RelationalOperator::Equal => KiwiRelOp::EQ,
},
None => KiwiRelOp::EQ,
}
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_violated(c: *const KiwiConstraint) -> bool {
match not_null_ref(c) {
Some(c) => {
let e = c.expr();
let value = e
.terms
.iter()
.map(|t| t.variable.value_ * t.coefficient.into_inner())
.sum::<f64>()
+ e.constant.into_inner();
match c.op() {
RelationalOperator::LessOrEqual => value > 0.0,
RelationalOperator::GreaterOrEqual => value < 0.0,
RelationalOperator::Equal => value.abs() > 1.0e-8,
}
}
None => false,
}
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_constraint_expression(
c: *const KiwiConstraint,
out: *mut KiwiExpressionPtr,
out_size: c_int,
) -> c_int {
match not_null_ref(c) {
Some(c) => {
let expr = c.expr();
let n_terms = expr.terms.len().min(c_int::MAX as usize) as c_int;
if out.is_null() || out_size < n_terms {
return n_terms;
}
let out = core::slice::from_raw_parts_mut(out as *mut (), n_terms as usize)
as *mut [()] as *mut KiwiExpression;

(*out).owner = out as *mut c_void;
(*out).term_count = n_terms;
(*out).constant = expr.constant.into();
for (o, i) in (*out).terms.iter_mut().zip(expr.terms.iter()) {
o.var = i.variable.retain_raw();
o.coefficient = i.coefficient.into();
}
n_terms
}
None => 0,
}
}
57 changes: 57 additions & 0 deletions rjkiwi/src/expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::ffi::{c_double, c_int, c_void};

use crate::*;

pub type KiwiExpressionPtr = expr::KiwiExpression<[expr::KiwiTerm; 0]>;

#[repr(C)]
#[derive(Debug)]
pub struct KiwiTerm {
pub var: *mut KiwiVar,
pub coefficient: c_double,
}

#[repr(C)]
#[derive(Debug)]
pub struct KiwiExpression<T: ?Sized = [KiwiTerm]> {
pub constant: c_double,
pub term_count: c_int,
pub owner: *mut c_void,
pub terms: T,
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_expression_retain(e: *mut KiwiExpressionPtr) {
if let Some(e) = not_null(e) {
let e = core::slice::from_raw_parts_mut(e as *mut (), (*e).term_count.max(0) as usize)
as *mut [()] as *mut KiwiExpression;

(*e).terms.iter().for_each(|t| {
if let Some(var) = not_null_mut(t.var) {
Rc::increment_strong_count(var);
}
});
(*e).owner = e as *mut c_void;
}
}

#[no_mangle]
pub unsafe extern "C" fn kiwi_expression_destroy(e: *mut KiwiExpressionPtr) {
if let Some(e) = not_null_mut(e) {
let e = core::slice::from_raw_parts_mut(e as *mut (), (*e).term_count.max(0) as usize)
as *mut [()] as *mut KiwiExpression;
match (*e).owner {
expr if expr == e as *mut c_void => {
(*e).terms.iter().for_each(|t| {
if let Some(var) = not_null_mut(t.var) {
Rc::decrement_strong_count(var);
}
});
}
c if !c.is_null() => {
Rc::decrement_strong_count(c as *mut KiwiConstraint);
}
_ => (),
}
}
}
71 changes: 71 additions & 0 deletions rjkiwi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::{
ffi::{c_char, CString},
rc::Rc,
};

use casuarius::Constraint;
use mimalloc::MiMalloc;

#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;

mod constraint;
mod expr;
mod solver;
mod var;

type KiwiVar = var::VarData;
type KiwiConstraint = Constraint<var::SolverVariable>;

unsafe fn as_solver_var(ptr: *const KiwiVar) -> Option<var::SolverVariable> {
if ptr.is_null() {
None
} else {
Some(var::SolverVariable::from_raw(ptr))
}
}

fn not_null<T: ?Sized>(ptr: *const T) -> Option<*const T> {
if ptr.is_null() {
None
} else {
Some(ptr)
}
}

fn not_null_mut<T: ?Sized>(ptr: *mut T) -> Option<*mut T> {
if ptr.is_null() {
None
} else {
Some(ptr)
}
}

unsafe fn not_null_ref<T>(ptr: *const T) -> Option<&'static T>
where
T: ?Sized,
{
if ptr.is_null() {
None
} else {
Some(&*ptr)
}
}

unsafe fn not_null_ref_mut<T>(ptr: *mut T) -> Option<&'static mut T>
where
T: ?Sized,
{
if ptr.is_null() {
None
} else {
Some(&mut *ptr)
}
}

#[no_mangle]
unsafe extern "C" fn kiwi_str_release(p: *mut c_char) {
if let Some(p) = not_null_mut(p) {
drop(CString::from_raw(p));
}
}
Loading

0 comments on commit 62bfd88

Please sign in to comment.