Guard against most egregious mistakes in calling the library
LuaJIT FFI is not inherently memory safe and there is no way to completely guard against the caller doing something that will trample over memory, but we can get pretty close. Biggest issue is that an empty table will stand-in for a ref struct with a null ref. So check for that in all the calls. In the calls that raise errors we now have a specific error for it. In the other functions the "nil" object is handled quietly but without a nullptr dereference and hopefully no UB.
This commit is contained in:
@@ -27,7 +27,7 @@ BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 90
|
||||
ColumnLimit: 98
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
|
||||
399
ckiwi/ckiwi.cpp
399
ckiwi/ckiwi.cpp
@@ -2,104 +2,206 @@
|
||||
|
||||
#include <kiwi/kiwi.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using namespace kiwi;
|
||||
|
||||
namespace {
|
||||
template<class T, class CS>
|
||||
class alignas(T) SharedRef {
|
||||
|
||||
template<typename T, typename R, typename... Args>
|
||||
inline R to_cref(Args&&... args) {
|
||||
static_assert(
|
||||
sizeof(R) >= sizeof(T), //NOLINT(bugprone-sizeof-expression)
|
||||
"to_cref: R too small for T"
|
||||
);
|
||||
static_assert(alignof(R) >= alignof(T), "to_cref: R alignment too small for T");
|
||||
|
||||
R cref;
|
||||
new (&cref) T(std::forward<Args>(args)...);
|
||||
return cref;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline decltype(auto) to_var_cref(Args&&... args) {
|
||||
return to_cref<Variable, KiwiVarRef>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline decltype(auto) to_constraint_cref(Args&&... args) {
|
||||
return to_cref<Constraint, KiwiConstraintRef>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class T, class R>
|
||||
class SharedRef {
|
||||
private:
|
||||
CS ref_;
|
||||
R& cref_;
|
||||
|
||||
public:
|
||||
T* operator&() {
|
||||
return reinterpret_cast<T*>(&ref_);
|
||||
explicit SharedRef<T, R>(R& cref) : cref_(cref) {}
|
||||
|
||||
static_assert(
|
||||
sizeof(R) >= sizeof(T), //NOLINT(bugprone-sizeof-expression)
|
||||
"SharedRef<T,CS> CS too small for T"
|
||||
);
|
||||
|
||||
void destroy() {
|
||||
if (cref_) {
|
||||
ptr()->~T();
|
||||
cref_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T* ptr() {
|
||||
T* p;
|
||||
void* s = &cref_;
|
||||
std::memcpy(&p, &s, sizeof p); //NOLINT(bugprone-sizeof-expression)
|
||||
return p;
|
||||
}
|
||||
|
||||
const T* const_ptr() const {
|
||||
const T* p;
|
||||
const void* s = &cref_;
|
||||
std::memcpy(&p, &s, sizeof p); //NOLINT(bugprone-sizeof-expression)
|
||||
return p;
|
||||
}
|
||||
|
||||
const T& cref() const {
|
||||
return *const_ptr();
|
||||
}
|
||||
|
||||
T* operator&() const {
|
||||
return ptr();
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return reinterpret_cast<T*>(&ref_);
|
||||
return ptr();
|
||||
}
|
||||
|
||||
operator T&() {
|
||||
return *reinterpret_cast<T*>(&ref_);
|
||||
const T* operator->() const {
|
||||
return const_ptr();
|
||||
}
|
||||
|
||||
operator CS() const {
|
||||
return ref_;
|
||||
operator const T&() const {
|
||||
return cref();
|
||||
}
|
||||
|
||||
T& instance() {
|
||||
return *reinterpret_cast<T*>(&ref_);
|
||||
explicit operator bool() const {
|
||||
return cref_;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
instance().~T();
|
||||
ref_ = {0};
|
||||
}
|
||||
|
||||
SharedRef<T, CS>(CS ref) : ref_(ref) {}
|
||||
|
||||
template<typename... Args>
|
||||
SharedRef<T, CS>(Args&&... args) {
|
||||
new (&ref_) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static_assert(sizeof(CS) >= sizeof(T), "SharedRef cannot wrap T (size)");
|
||||
};
|
||||
|
||||
using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
|
||||
using VariableRef = SharedRef<Variable, KiwiVarRef>;
|
||||
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
|
||||
|
||||
KiwiErr make_error(KiwiErrKind kind, const std::exception& ex) {
|
||||
constexpr auto max_n = sizeof(KiwiErr::message) - 1;
|
||||
const auto n = std::min(std::strlen(ex.what()), max_n);
|
||||
KiwiErrPtr new_error(KiwiErrPtr base, const std::exception& ex) {
|
||||
if (!std::strcmp(ex.what(), base->message))
|
||||
return base;
|
||||
|
||||
KiwiErr err {kind};
|
||||
const auto msg_n = std::strlen(ex.what()) + 1;
|
||||
|
||||
std::memcpy(err.message, ex.what(), n);
|
||||
if (n == max_n)
|
||||
err.message[max_n] = 0;
|
||||
auto* mem = static_cast<char*>(std::malloc(sizeof(KiwiErr) + msg_n));
|
||||
if (!mem) {
|
||||
return base;
|
||||
}
|
||||
|
||||
const auto* err = new (mem) KiwiErr {base->kind, mem + sizeof(KiwiErr), true};
|
||||
std::memcpy(const_cast<char*>(err->message), ex.what(), msg_n);
|
||||
return err;
|
||||
}
|
||||
|
||||
KiwiErr make_error(KiwiErrKind kind) {
|
||||
return KiwiErr {kind, {0}};
|
||||
}
|
||||
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||
KiwiErrUnknown,
|
||||
"An unhandled C++ exception occurred."};
|
||||
|
||||
static const constexpr KiwiErr kKiwiErrNullObjectArg0 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #0 (self)"};
|
||||
|
||||
static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #1"};
|
||||
|
||||
template<typename F>
|
||||
inline KiwiErr wrap_err(F&& f) {
|
||||
inline KiwiErrPtr wrap_err(F&& f) {
|
||||
try {
|
||||
f();
|
||||
} catch (const UnsatisfiableConstraint& err) {
|
||||
return make_error(KiwiErrUnsatisfiableConstraint, err);
|
||||
} catch (const UnknownConstraint& err) {
|
||||
return make_error(KiwiErrUnknownConstraint, err);
|
||||
} catch (const DuplicateConstraint& err) {
|
||||
return make_error(KiwiErrDuplicateConstraint, err);
|
||||
} catch (const UnknownEditVariable& err) {
|
||||
return make_error(KiwiErrUnknownEditVariable, err);
|
||||
} catch (const DuplicateEditVariable& err) {
|
||||
return make_error(KiwiErrDuplicateEditVariable, err);
|
||||
} catch (const BadRequiredStrength& err) {
|
||||
return make_error(KiwiErrBadRequiredStrength, err);
|
||||
} catch (const InternalSolverError& err) {
|
||||
return make_error(KiwiErrInternalSolverError, err);
|
||||
} catch (const UnsatisfiableConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnsatisfiableConstraint,
|
||||
"The constraint cannot be satisfied."};
|
||||
return &err;
|
||||
} catch (const UnknownConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownConstraint,
|
||||
"The constraint has not been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateConstraint,
|
||||
"The constraint has already been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const UnknownEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownEditVariable,
|
||||
"The edit variable has not been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateEditVariable,
|
||||
"The edit variable has already been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const BadRequiredStrength& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrBadRequiredStrength,
|
||||
"A required strength cannot be used in this context."};
|
||||
return &err;
|
||||
|
||||
} catch (const InternalSolverError& ex) {
|
||||
static const constexpr KiwiErr base {
|
||||
KiwiErrInternalSolverError,
|
||||
"An internal solver error occurred."};
|
||||
return new_error(&base, ex);
|
||||
} catch (std::bad_alloc&) {
|
||||
return make_error(KiwiErrAlloc);
|
||||
} catch (const std::exception& err) {
|
||||
return make_error(KiwiErrUnknown, err);
|
||||
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
||||
return &err;
|
||||
} catch (const std::exception& ex) {
|
||||
return new_error(&kKiwiErrUnhandledCxxException, ex);
|
||||
} catch (...) {
|
||||
return make_error(KiwiErrUnknown);
|
||||
return &kKiwiErrUnhandledCxxException;
|
||||
}
|
||||
return make_error(KiwiErrNone);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename P, typename R, typename F>
|
||||
inline KiwiErrPtr wrap_err(P ptr, F&& f) {
|
||||
if (!ptr) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
}
|
||||
return wrap_err([&]() { f(ptr); });
|
||||
}
|
||||
|
||||
template<typename P, typename R, typename F>
|
||||
inline KiwiErrPtr wrap_err(P ptr, R ref, F&& f) {
|
||||
if (!ptr) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
} else if (!ref) {
|
||||
return &kKiwiErrNullObjectArg1;
|
||||
}
|
||||
return wrap_err([&]() { f(ptr, ref); });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
KiwiVarRef kiwi_var_new(const char* name) {
|
||||
return VariableRef(name);
|
||||
return to_var_cref(name ? name : "");
|
||||
}
|
||||
|
||||
void kiwi_var_del(KiwiVarRef var) {
|
||||
@@ -107,42 +209,50 @@ void kiwi_var_del(KiwiVarRef var) {
|
||||
}
|
||||
|
||||
const char* kiwi_var_name(KiwiVarRef var) {
|
||||
return VariableRef(var)->name().c_str();
|
||||
const VariableRef self(var);
|
||||
return self ? self->name().c_str() : "(<null>)";
|
||||
}
|
||||
|
||||
void kiwi_var_set_name(KiwiVarRef var, const char* name) {
|
||||
VariableRef(var)->setName(name);
|
||||
VariableRef self(var);
|
||||
if (self)
|
||||
self->setName(name);
|
||||
}
|
||||
|
||||
double kiwi_var_value(KiwiVarRef var) {
|
||||
return VariableRef(var)->value();
|
||||
const VariableRef self(var);
|
||||
return self ? self->value() : std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
void kiwi_var_set_value(KiwiVarRef var, double value) {
|
||||
VariableRef(var)->setValue(value);
|
||||
VariableRef self(var);
|
||||
if (self)
|
||||
self->setValue(value);
|
||||
}
|
||||
|
||||
int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) {
|
||||
return VariableRef(var)->equals(VariableRef(other));
|
||||
VariableRef self(var); // const defect in upstream
|
||||
const VariableRef other_ref(other);
|
||||
|
||||
return self && other_ref && self->equals(other_ref);
|
||||
}
|
||||
|
||||
KiwiConstraintRef kiwi_constraint_new(
|
||||
const KiwiExpression* expression,
|
||||
enum KiwiRelOp op,
|
||||
double strength
|
||||
) {
|
||||
KiwiConstraintRef
|
||||
kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength) {
|
||||
if (strength < 0.0) {
|
||||
strength = kiwi::strength::required;
|
||||
}
|
||||
std::vector<Term> terms;
|
||||
terms.reserve(expression->term_count);
|
||||
if (expression) {
|
||||
terms.reserve(expression->term_count);
|
||||
|
||||
for (auto* t = expression->terms; t != expression->terms + expression->term_count;
|
||||
++t) {
|
||||
terms.emplace_back(VariableRef(t->var), t->coefficient);
|
||||
for (auto* t = expression->terms; t != expression->terms + expression->term_count; ++t) {
|
||||
ConstVariableRef var(t->var);
|
||||
if (var)
|
||||
terms.emplace_back(var.cref(), t->coefficient);
|
||||
}
|
||||
}
|
||||
|
||||
return ConstraintRef(
|
||||
return to_constraint_cref(
|
||||
Expression(std::move(terms), expression->constant),
|
||||
static_cast<RelationalOperator>(op),
|
||||
strength
|
||||
@@ -154,23 +264,25 @@ void kiwi_constraint_del(KiwiConstraintRef constraint) {
|
||||
}
|
||||
|
||||
double kiwi_constraint_strength(KiwiConstraintRef constraint) {
|
||||
return ConstraintRef(constraint)->strength();
|
||||
const ConstraintRef self(constraint);
|
||||
return self ? self->strength() : std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint) {
|
||||
return static_cast<KiwiRelOp>(ConstraintRef(constraint)->op());
|
||||
const ConstraintRef self(constraint);
|
||||
return self ? static_cast<KiwiRelOp>(self->op()) : KiwiRelOp::KIWI_OP_EQ;
|
||||
}
|
||||
|
||||
int kiwi_constraint_violated(KiwiConstraintRef constraint) {
|
||||
return ConstraintRef(constraint)->violated();
|
||||
bool kiwi_constraint_violated(KiwiConstraintRef constraint) {
|
||||
const ConstraintRef self(constraint);
|
||||
return self ? self->violated() : 0;
|
||||
}
|
||||
|
||||
int kiwi_constraint_expression(
|
||||
KiwiConstraintRef constraint,
|
||||
KiwiExpression* out,
|
||||
int out_size
|
||||
) {
|
||||
const auto& expr = ConstraintRef(constraint).instance().expression();
|
||||
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size) {
|
||||
const ConstraintRef self(constraint);
|
||||
if (!self)
|
||||
return 0;
|
||||
const auto& expr = self->expression();
|
||||
const auto& terms = expr.terms();
|
||||
const int n_terms = terms.size();
|
||||
if (!out || out_size < n_terms)
|
||||
@@ -178,7 +290,7 @@ int kiwi_constraint_expression(
|
||||
|
||||
auto* p = out->terms;
|
||||
for (const auto& t : terms) {
|
||||
*p = KiwiTerm {VariableRef(t.variable()), t.coefficient()};
|
||||
*p = KiwiTerm {to_var_cref(t.variable()), t.coefficient()};
|
||||
++p;
|
||||
}
|
||||
out->term_count = p - out->terms;
|
||||
@@ -187,71 +299,100 @@ int kiwi_constraint_expression(
|
||||
return n_terms;
|
||||
}
|
||||
|
||||
KiwiSolverRef kiwi_solver_new() {
|
||||
return KiwiSolverRef {new (std::nothrow) Solver()};
|
||||
KiwiSolverPtr kiwi_solver_new() {
|
||||
return reinterpret_cast<KiwiSolverPtr>(new (std::nothrow) Solver());
|
||||
}
|
||||
|
||||
void kiwi_solver_del(KiwiSolverRef s) {
|
||||
delete reinterpret_cast<Solver*>(s.impl_);
|
||||
void kiwi_solver_del(KiwiSolverPtr sp) {
|
||||
auto* solver = reinterpret_cast<Solver*>(sp);
|
||||
if (solver)
|
||||
delete solver;
|
||||
}
|
||||
|
||||
KiwiErr kiwi_solver_add_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
||||
return wrap_err([=]() {
|
||||
reinterpret_cast<Solver*>(s.impl_)->addConstraint(ConstraintRef(constraint));
|
||||
KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) {
|
||||
return wrap_err(
|
||||
reinterpret_cast<Solver*>(s),
|
||||
ConstraintRef(constraint),
|
||||
[](auto solver, const auto c) { solver->addConstraint(c); }
|
||||
);
|
||||
}
|
||||
|
||||
KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) {
|
||||
return wrap_err(
|
||||
reinterpret_cast<Solver*>(s),
|
||||
ConstraintRef(constraint),
|
||||
[](auto solver, const auto c) { solver->removeConstraint(c); }
|
||||
);
|
||||
}
|
||||
|
||||
bool kiwi_solver_has_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) {
|
||||
const auto* solver = reinterpret_cast<Solver*>(s);
|
||||
ConstraintRef c(constraint);
|
||||
if (!solver || !c)
|
||||
return 0;
|
||||
return solver->hasConstraint(c);
|
||||
}
|
||||
|
||||
KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr s, KiwiVarRef var, double strength) {
|
||||
return wrap_err(
|
||||
reinterpret_cast<Solver*>(s),
|
||||
VariableRef(var),
|
||||
[strength](auto solver, const auto v) { solver->addEditVariable(v, strength); }
|
||||
);
|
||||
}
|
||||
|
||||
KiwiErrPtr kiwi_solver_remove_edit_var(KiwiSolverPtr s, KiwiVarRef var) {
|
||||
return wrap_err(reinterpret_cast<Solver*>(s), VariableRef(var), [](auto solver, const auto v) {
|
||||
solver->removeEditVariable(v);
|
||||
});
|
||||
}
|
||||
|
||||
KiwiErr kiwi_solver_remove_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
||||
return wrap_err([=]() {
|
||||
reinterpret_cast<Solver*>(s.impl_)->removeConstraint(ConstraintRef(constraint));
|
||||
});
|
||||
bool kiwi_solver_has_edit_var(KiwiSolverPtr s, KiwiVarRef var) {
|
||||
const auto* solver = reinterpret_cast<Solver*>(s);
|
||||
VariableRef v(var);
|
||||
if (!solver || !v)
|
||||
return 0;
|
||||
return solver->hasEditVariable(v);
|
||||
}
|
||||
|
||||
int kiwi_solver_has_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
||||
return reinterpret_cast<Solver*>(s.impl_)->hasConstraint(ConstraintRef(constraint));
|
||||
KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr s, KiwiVarRef var, double value) {
|
||||
return wrap_err(
|
||||
reinterpret_cast<Solver*>(s),
|
||||
VariableRef(var),
|
||||
|
||||
[value](auto solver, const auto v) { solver->suggestValue(v, value); }
|
||||
);
|
||||
}
|
||||
|
||||
KiwiErr kiwi_solver_add_edit_var(KiwiSolverRef s, KiwiVarRef var, double strength) {
|
||||
return wrap_err([=]() {
|
||||
reinterpret_cast<Solver*>(s.impl_)->addEditVariable(VariableRef(var), strength);
|
||||
});
|
||||
void kiwi_solver_update_vars(KiwiSolverPtr s) {
|
||||
auto* solver = reinterpret_cast<Solver*>(s);
|
||||
if (solver)
|
||||
solver->updateVariables();
|
||||
}
|
||||
|
||||
KiwiErr kiwi_solver_remove_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
||||
return wrap_err([=]() {
|
||||
reinterpret_cast<Solver*>(s.impl_)->removeEditVariable(VariableRef(var));
|
||||
});
|
||||
void kiwi_solver_reset(KiwiSolverPtr s) {
|
||||
auto* solver = reinterpret_cast<Solver*>(s);
|
||||
if (solver)
|
||||
solver->reset();
|
||||
}
|
||||
|
||||
int kiwi_solver_has_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
||||
return reinterpret_cast<Solver*>(s.impl_)->hasEditVariable(VariableRef(var));
|
||||
void kiwi_solver_dump(KiwiSolverPtr s) {
|
||||
auto* solver = reinterpret_cast<Solver*>(s);
|
||||
if (solver)
|
||||
solver->dump();
|
||||
}
|
||||
|
||||
KiwiErr kiwi_solver_suggest_value(KiwiSolverRef s, KiwiVarRef var, double value) {
|
||||
return wrap_err([=]() {
|
||||
reinterpret_cast<Solver*>(s.impl_)->suggestValue(VariableRef(var), value);
|
||||
});
|
||||
}
|
||||
char* kiwi_solver_dumps(KiwiSolverPtr s) {
|
||||
auto* solver = reinterpret_cast<Solver*>(s);
|
||||
if (!solver)
|
||||
return nullptr;
|
||||
|
||||
void kiwi_solver_update_vars(KiwiSolverRef s) {
|
||||
reinterpret_cast<Solver*>(s.impl_)->updateVariables();
|
||||
}
|
||||
|
||||
void kiwi_solver_reset(KiwiSolverRef s) {
|
||||
reinterpret_cast<Solver*>(s.impl_)->reset();
|
||||
}
|
||||
|
||||
void kiwi_solver_dump(KiwiSolverRef s) {
|
||||
reinterpret_cast<Solver*>(s.impl_)->dump();
|
||||
}
|
||||
|
||||
char* kiwi_solver_dumps(KiwiSolverRef s, void* (*alloc)(size_t)) {
|
||||
const auto val = reinterpret_cast<Solver*>(s.impl_)->dumps();
|
||||
const auto buf_size = val.size() + 1;
|
||||
auto* buf = static_cast<char*>(alloc ? alloc(buf_size) : malloc(buf_size));
|
||||
const auto str = solver->dumps();
|
||||
const auto buf_size = str.size() + 1;
|
||||
auto* buf = static_cast<char*>(std::malloc(buf_size));
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
std::memcpy(buf, val.c_str(), val.size() + 1);
|
||||
std::memcpy(buf, str.c_str(), str.size() + 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,38 +20,39 @@ enum KiwiErrKind {
|
||||
KiwiErrBadRequiredStrength,
|
||||
KiwiErrInternalSolverError,
|
||||
KiwiErrAlloc,
|
||||
KiwiErrNullObject,
|
||||
KiwiErrUnknown,
|
||||
};
|
||||
|
||||
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
|
||||
|
||||
typedef struct {
|
||||
void* private_;
|
||||
} KiwiVarRef;
|
||||
struct KiwiVarRefType;
|
||||
struct KiwiConstraintRefType;
|
||||
|
||||
typedef struct {
|
||||
typedef struct KiwiVarRefType* KiwiVarRef;
|
||||
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
|
||||
|
||||
struct KiwiTerm {
|
||||
KiwiVarRef var;
|
||||
double coefficient;
|
||||
} KiwiTerm;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
KiwiTerm terms[1];
|
||||
} KiwiExpression;
|
||||
struct KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?];
|
||||
}* KiwiExpressionPtr;
|
||||
|
||||
typedef struct {
|
||||
void* private_;
|
||||
} KiwiConstraintRef;
|
||||
typedef const struct KiwiExpression* KiwiExpressionConstPtr;
|
||||
|
||||
typedef struct {
|
||||
typedef struct KiwiErr {
|
||||
enum KiwiErrKind kind;
|
||||
char message[64];
|
||||
} KiwiErr;
|
||||
const char* message;
|
||||
bool must_free;
|
||||
} const* KiwiErrPtr;
|
||||
|
||||
typedef struct {
|
||||
void* impl_;
|
||||
} KiwiSolverRef;
|
||||
struct KiwiSolver;
|
||||
typedef struct KiwiSolver* KiwiSolverPtr;
|
||||
|
||||
KiwiVarRef kiwi_var_new(const char* name);
|
||||
void kiwi_var_del(KiwiVarRef var);
|
||||
@@ -67,45 +68,30 @@ void kiwi_var_set_value(KiwiVarRef var, double value);
|
||||
int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
|
||||
|
||||
KiwiConstraintRef
|
||||
kiwi_constraint_new(const KiwiExpression* expression, enum KiwiRelOp op, double strength);
|
||||
kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength);
|
||||
void kiwi_constraint_del(KiwiConstraintRef constraint);
|
||||
|
||||
double kiwi_constraint_strength(KiwiConstraintRef constraint);
|
||||
|
||||
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint);
|
||||
|
||||
int kiwi_constraint_violated(KiwiConstraintRef constraint);
|
||||
bool kiwi_constraint_violated(KiwiConstraintRef constraint);
|
||||
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpressionPtr out, int out_size);
|
||||
|
||||
int kiwi_constraint_expression(
|
||||
KiwiConstraintRef constraint,
|
||||
KiwiExpression* out,
|
||||
int out_size
|
||||
);
|
||||
KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||
KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||
bool kiwi_solver_has_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||
KiwiErrPtr kiwi_solver_add_edit_var(KiwiSolverPtr sp, KiwiVarRef var, double strength);
|
||||
KiwiErrPtr kiwi_solver_remove_edit_var(KiwiSolverPtr sp, KiwiVarRef var);
|
||||
bool kiwi_solver_has_edit_var(KiwiSolverPtr sp, KiwiVarRef var);
|
||||
KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr sp, KiwiVarRef var, double value);
|
||||
void kiwi_solver_update_vars(KiwiSolverPtr sp);
|
||||
void kiwi_solver_reset(KiwiSolverPtr sp);
|
||||
void kiwi_solver_dump(KiwiSolverPtr sp);
|
||||
char* kiwi_solver_dumps(KiwiSolverPtr sp);
|
||||
|
||||
KiwiErr kiwi_solver_add_constraint(KiwiSolverRef s, KiwiConstraintRef constraint);
|
||||
|
||||
KiwiErr kiwi_solver_remove_constraint(KiwiSolverRef s, KiwiConstraintRef constraint);
|
||||
|
||||
int kiwi_solver_has_constraint(KiwiSolverRef s, KiwiConstraintRef constraint);
|
||||
|
||||
KiwiErr kiwi_solver_add_edit_var(KiwiSolverRef s, KiwiVarRef var, double strength);
|
||||
|
||||
KiwiErr kiwi_solver_remove_edit_var(KiwiSolverRef s, KiwiVarRef var);
|
||||
|
||||
int kiwi_solver_has_edit_var(KiwiSolverRef s, KiwiVarRef var);
|
||||
|
||||
KiwiErr kiwi_solver_suggest_value(KiwiSolverRef s, KiwiVarRef var, double value);
|
||||
|
||||
void kiwi_solver_update_vars(KiwiSolverRef s);
|
||||
|
||||
void kiwi_solver_reset(KiwiSolverRef s);
|
||||
|
||||
void kiwi_solver_dump(KiwiSolverRef s);
|
||||
|
||||
char* kiwi_solver_dumps(KiwiSolverRef s, void* (*alloc)(size_t));
|
||||
|
||||
KiwiSolverRef kiwi_solver_new();
|
||||
void kiwi_solver_del(KiwiSolverRef s);
|
||||
KiwiSolverPtr kiwi_solver_new();
|
||||
void kiwi_solver_del(KiwiSolverPtr sp);
|
||||
// LuaJIT end
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user