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:
@@ -8,5 +8,6 @@
|
|||||||
"lua_modules/share/lua/5.1/?.lua",
|
"lua_modules/share/lua/5.1/?.lua",
|
||||||
"lua_modules/share/lua/5.1/?/init.lua"
|
"lua_modules/share/lua/5.1/?/init.lua"
|
||||||
],
|
],
|
||||||
|
"workspace.library": ["${3rd}/busted/library", "${3rd}/luassert/library"],
|
||||||
"workspace.checkThirdParty": false
|
"workspace.checkThirdParty": false
|
||||||
}
|
}
|
||||||
|
|||||||
23
Makefile
23
Makefile
@@ -1,22 +1,29 @@
|
|||||||
SRCDIR ?= .
|
SRCDIR := .
|
||||||
CC ?= $(CROSS)gcc
|
CC := $(CROSS)gcc
|
||||||
CFLAGS ?= -fPIC -O2
|
CFLAGS := -fPIC -O2
|
||||||
CFLAGS += -Wall -I$(SRCDIR)/kiwi
|
CFLAGS += -Wall -I$(SRCDIR)/kiwi
|
||||||
LIBFLAG ?= -shared
|
LIBFLAG := -shared
|
||||||
LIB_EXT ?= so
|
LIB_EXT := so
|
||||||
|
|
||||||
ifeq ($(findstring gcc, $(CC)), gcc)
|
ifeq ($(findstring gcc, $(CC)), gcc)
|
||||||
CXX := $(subst gcc, g++, $(CC))
|
CXX := $(subst gcc, g++, $(CC))
|
||||||
CXXFLAGS += -std=c++11
|
CXXFLAGS += -std=c++14
|
||||||
|
ifneq ($(SANITIZE),)
|
||||||
|
CFLAGS += -fsanitize=undefined -fsanitize=address
|
||||||
|
endif
|
||||||
else
|
else
|
||||||
ifeq ($(CC), clang)
|
ifeq ($(CC), clang)
|
||||||
CXX := clang++
|
CXX := clang++
|
||||||
CXXFLAGS += -std=c++11
|
CXXFLAGS += -std=c++14
|
||||||
|
ifneq ($(SANITIZE),)
|
||||||
|
CFLAGS += -fsanitize=undefined -fsanitize=address
|
||||||
|
endif
|
||||||
else
|
else
|
||||||
CXX := $(CC)
|
CXX := $(CC)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
all: ckiwi.$(LIB_EXT)
|
all: ckiwi.$(LIB_EXT)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@@ -26,7 +33,7 @@ install:
|
|||||||
clean:
|
clean:
|
||||||
rm -f ckiwi.$(LIB_EXT)
|
rm -f ckiwi.$(LIB_EXT)
|
||||||
|
|
||||||
ckiwi.$(LIB_EXT): $(SRCDIR)/ckiwi/ckiwi.cpp
|
ckiwi.$(LIB_EXT): $(SRCDIR)/ckiwi/ckiwi.cpp $(SRCDIR)/ckiwi/ckiwi.h
|
||||||
$(CXX) $(CXXFLAGS) $(CFLAGS) -fPIC -Wall -I$(SRCDIR)/kiwi $(LIBFLAG) -o $@ $<
|
$(CXX) $(CXXFLAGS) $(CFLAGS) -fPIC -Wall -I$(SRCDIR)/kiwi $(LIBFLAG) -o $@ $<
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Cassowary constraint solving is a technique that is particularly well suited to
|
|||||||
|
|
||||||
There are a few Lua implementations or attempts. The SILE typesetting system has a pure Lua implementation of the original Cassowary code, which appears to be correct but is quite slow. There are two extant Lua ports of Kiwi, one that is based on a C rewrite of Kiwi. However testing of these was not encouraging with either segfaults or incorrect results.
|
There are a few Lua implementations or attempts. The SILE typesetting system has a pure Lua implementation of the original Cassowary code, which appears to be correct but is quite slow. There are two extant Lua ports of Kiwi, one that is based on a C rewrite of Kiwi. However testing of these was not encouraging with either segfaults or incorrect results.
|
||||||
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper and use that.
|
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper and use that.
|
||||||
This package has no dependencies other than a C++11 toolchain to compile the included Kiwi library and a small C wrapper.
|
This package has no dependencies other than a C++14 toolchain to compile the included Kiwi library and a small C wrapper.
|
||||||
|
|
||||||
The Lua API has a pure Lua expression builder. There is of course some overhead to this, however in most cases expression building is infrequent and the underlying structures can be reused.
|
The Lua API has a pure Lua expression builder. There is of course some overhead to this, however in most cases expression building is infrequent and the underlying structures can be reused.
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ BreakBeforeTernaryOperators: true
|
|||||||
BreakConstructorInitializers: AfterColon
|
BreakConstructorInitializers: AfterColon
|
||||||
BreakInheritanceList: AfterColon
|
BreakInheritanceList: AfterColon
|
||||||
BreakStringLiterals: false
|
BreakStringLiterals: false
|
||||||
ColumnLimit: 90
|
ColumnLimit: 98
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
|||||||
399
ckiwi/ckiwi.cpp
399
ckiwi/ckiwi.cpp
@@ -2,104 +2,206 @@
|
|||||||
|
|
||||||
#include <kiwi/kiwi.h>
|
#include <kiwi/kiwi.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
using namespace kiwi;
|
using namespace kiwi;
|
||||||
|
|
||||||
namespace {
|
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:
|
private:
|
||||||
CS ref_;
|
R& cref_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
T* operator&() {
|
explicit SharedRef<T, R>(R& cref) : cref_(cref) {}
|
||||||
return reinterpret_cast<T*>(&ref_);
|
|
||||||
|
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->() {
|
T* operator->() {
|
||||||
return reinterpret_cast<T*>(&ref_);
|
return ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
operator T&() {
|
const T* operator->() const {
|
||||||
return *reinterpret_cast<T*>(&ref_);
|
return const_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
operator CS() const {
|
operator const T&() const {
|
||||||
return ref_;
|
return cref();
|
||||||
}
|
}
|
||||||
|
|
||||||
T& instance() {
|
explicit operator bool() const {
|
||||||
return *reinterpret_cast<T*>(&ref_);
|
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 ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
|
||||||
using VariableRef = SharedRef<Variable, KiwiVarRef>;
|
using VariableRef = SharedRef<Variable, KiwiVarRef>;
|
||||||
|
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
|
||||||
|
|
||||||
KiwiErr make_error(KiwiErrKind kind, const std::exception& ex) {
|
KiwiErrPtr new_error(KiwiErrPtr base, const std::exception& ex) {
|
||||||
constexpr auto max_n = sizeof(KiwiErr::message) - 1;
|
if (!std::strcmp(ex.what(), base->message))
|
||||||
const auto n = std::min(std::strlen(ex.what()), max_n);
|
return base;
|
||||||
|
|
||||||
KiwiErr err {kind};
|
const auto msg_n = std::strlen(ex.what()) + 1;
|
||||||
|
|
||||||
std::memcpy(err.message, ex.what(), n);
|
auto* mem = static_cast<char*>(std::malloc(sizeof(KiwiErr) + msg_n));
|
||||||
if (n == max_n)
|
if (!mem) {
|
||||||
err.message[max_n] = 0;
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
KiwiErr make_error(KiwiErrKind kind) {
|
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||||
return KiwiErr {kind, {0}};
|
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>
|
template<typename F>
|
||||||
inline KiwiErr wrap_err(F&& f) {
|
inline KiwiErrPtr wrap_err(F&& f) {
|
||||||
try {
|
try {
|
||||||
f();
|
f();
|
||||||
} catch (const UnsatisfiableConstraint& err) {
|
} catch (const UnsatisfiableConstraint& ex) {
|
||||||
return make_error(KiwiErrUnsatisfiableConstraint, err);
|
static const constexpr KiwiErr err {
|
||||||
} catch (const UnknownConstraint& err) {
|
KiwiErrUnsatisfiableConstraint,
|
||||||
return make_error(KiwiErrUnknownConstraint, err);
|
"The constraint cannot be satisfied."};
|
||||||
} catch (const DuplicateConstraint& err) {
|
return &err;
|
||||||
return make_error(KiwiErrDuplicateConstraint, err);
|
} catch (const UnknownConstraint& ex) {
|
||||||
} catch (const UnknownEditVariable& err) {
|
static const constexpr KiwiErr err {
|
||||||
return make_error(KiwiErrUnknownEditVariable, err);
|
KiwiErrUnknownConstraint,
|
||||||
} catch (const DuplicateEditVariable& err) {
|
"The constraint has not been added to the solver."};
|
||||||
return make_error(KiwiErrDuplicateEditVariable, err);
|
return &err;
|
||||||
} catch (const BadRequiredStrength& err) {
|
|
||||||
return make_error(KiwiErrBadRequiredStrength, err);
|
} catch (const DuplicateConstraint& ex) {
|
||||||
} catch (const InternalSolverError& err) {
|
static const constexpr KiwiErr err {
|
||||||
return make_error(KiwiErrInternalSolverError, 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&) {
|
} catch (std::bad_alloc&) {
|
||||||
return make_error(KiwiErrAlloc);
|
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
||||||
} catch (const std::exception& err) {
|
return &err;
|
||||||
return make_error(KiwiErrUnknown, err);
|
} catch (const std::exception& ex) {
|
||||||
|
return new_error(&kKiwiErrUnhandledCxxException, ex);
|
||||||
} catch (...) {
|
} 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
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
KiwiVarRef kiwi_var_new(const char* name) {
|
KiwiVarRef kiwi_var_new(const char* name) {
|
||||||
return VariableRef(name);
|
return to_var_cref(name ? name : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_var_del(KiwiVarRef var) {
|
void kiwi_var_del(KiwiVarRef var) {
|
||||||
@@ -107,42 +209,50 @@ void kiwi_var_del(KiwiVarRef var) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char* kiwi_var_name(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) {
|
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) {
|
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) {
|
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) {
|
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(
|
KiwiConstraintRef
|
||||||
const KiwiExpression* expression,
|
kiwi_constraint_new(KiwiExpressionConstPtr expression, enum KiwiRelOp op, double strength) {
|
||||||
enum KiwiRelOp op,
|
|
||||||
double strength
|
|
||||||
) {
|
|
||||||
if (strength < 0.0) {
|
if (strength < 0.0) {
|
||||||
strength = kiwi::strength::required;
|
strength = kiwi::strength::required;
|
||||||
}
|
}
|
||||||
std::vector<Term> terms;
|
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;
|
for (auto* t = expression->terms; t != expression->terms + expression->term_count; ++t) {
|
||||||
++t) {
|
ConstVariableRef var(t->var);
|
||||||
terms.emplace_back(VariableRef(t->var), t->coefficient);
|
if (var)
|
||||||
|
terms.emplace_back(var.cref(), t->coefficient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return to_constraint_cref(
|
||||||
return ConstraintRef(
|
|
||||||
Expression(std::move(terms), expression->constant),
|
Expression(std::move(terms), expression->constant),
|
||||||
static_cast<RelationalOperator>(op),
|
static_cast<RelationalOperator>(op),
|
||||||
strength
|
strength
|
||||||
@@ -154,23 +264,25 @@ void kiwi_constraint_del(KiwiConstraintRef constraint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double kiwi_constraint_strength(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) {
|
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) {
|
bool kiwi_constraint_violated(KiwiConstraintRef constraint) {
|
||||||
return ConstraintRef(constraint)->violated();
|
const ConstraintRef self(constraint);
|
||||||
|
return self ? self->violated() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kiwi_constraint_expression(
|
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size) {
|
||||||
KiwiConstraintRef constraint,
|
const ConstraintRef self(constraint);
|
||||||
KiwiExpression* out,
|
if (!self)
|
||||||
int out_size
|
return 0;
|
||||||
) {
|
const auto& expr = self->expression();
|
||||||
const auto& expr = ConstraintRef(constraint).instance().expression();
|
|
||||||
const auto& terms = expr.terms();
|
const auto& terms = expr.terms();
|
||||||
const int n_terms = terms.size();
|
const int n_terms = terms.size();
|
||||||
if (!out || out_size < n_terms)
|
if (!out || out_size < n_terms)
|
||||||
@@ -178,7 +290,7 @@ int kiwi_constraint_expression(
|
|||||||
|
|
||||||
auto* p = out->terms;
|
auto* p = out->terms;
|
||||||
for (const auto& t : terms) {
|
for (const auto& t : terms) {
|
||||||
*p = KiwiTerm {VariableRef(t.variable()), t.coefficient()};
|
*p = KiwiTerm {to_var_cref(t.variable()), t.coefficient()};
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
out->term_count = p - out->terms;
|
out->term_count = p - out->terms;
|
||||||
@@ -187,71 +299,100 @@ int kiwi_constraint_expression(
|
|||||||
return n_terms;
|
return n_terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
KiwiSolverRef kiwi_solver_new() {
|
KiwiSolverPtr kiwi_solver_new() {
|
||||||
return KiwiSolverRef {new (std::nothrow) Solver()};
|
return reinterpret_cast<KiwiSolverPtr>(new (std::nothrow) Solver());
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_solver_del(KiwiSolverRef s) {
|
void kiwi_solver_del(KiwiSolverPtr sp) {
|
||||||
delete reinterpret_cast<Solver*>(s.impl_);
|
auto* solver = reinterpret_cast<Solver*>(sp);
|
||||||
|
if (solver)
|
||||||
|
delete solver;
|
||||||
}
|
}
|
||||||
|
|
||||||
KiwiErr kiwi_solver_add_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr s, KiwiConstraintRef constraint) {
|
||||||
return wrap_err([=]() {
|
return wrap_err(
|
||||||
reinterpret_cast<Solver*>(s.impl_)->addConstraint(ConstraintRef(constraint));
|
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) {
|
bool kiwi_solver_has_edit_var(KiwiSolverPtr s, KiwiVarRef var) {
|
||||||
return wrap_err([=]() {
|
const auto* solver = reinterpret_cast<Solver*>(s);
|
||||||
reinterpret_cast<Solver*>(s.impl_)->removeConstraint(ConstraintRef(constraint));
|
VariableRef v(var);
|
||||||
});
|
if (!solver || !v)
|
||||||
|
return 0;
|
||||||
|
return solver->hasEditVariable(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kiwi_solver_has_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
KiwiErrPtr kiwi_solver_suggest_value(KiwiSolverPtr s, KiwiVarRef var, double value) {
|
||||||
return reinterpret_cast<Solver*>(s.impl_)->hasConstraint(ConstraintRef(constraint));
|
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) {
|
void kiwi_solver_update_vars(KiwiSolverPtr s) {
|
||||||
return wrap_err([=]() {
|
auto* solver = reinterpret_cast<Solver*>(s);
|
||||||
reinterpret_cast<Solver*>(s.impl_)->addEditVariable(VariableRef(var), strength);
|
if (solver)
|
||||||
});
|
solver->updateVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
KiwiErr kiwi_solver_remove_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
void kiwi_solver_reset(KiwiSolverPtr s) {
|
||||||
return wrap_err([=]() {
|
auto* solver = reinterpret_cast<Solver*>(s);
|
||||||
reinterpret_cast<Solver*>(s.impl_)->removeEditVariable(VariableRef(var));
|
if (solver)
|
||||||
});
|
solver->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int kiwi_solver_has_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
void kiwi_solver_dump(KiwiSolverPtr s) {
|
||||||
return reinterpret_cast<Solver*>(s.impl_)->hasEditVariable(VariableRef(var));
|
auto* solver = reinterpret_cast<Solver*>(s);
|
||||||
|
if (solver)
|
||||||
|
solver->dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
KiwiErr kiwi_solver_suggest_value(KiwiSolverRef s, KiwiVarRef var, double value) {
|
char* kiwi_solver_dumps(KiwiSolverPtr s) {
|
||||||
return wrap_err([=]() {
|
auto* solver = reinterpret_cast<Solver*>(s);
|
||||||
reinterpret_cast<Solver*>(s.impl_)->suggestValue(VariableRef(var), value);
|
if (!solver)
|
||||||
});
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void kiwi_solver_update_vars(KiwiSolverRef s) {
|
const auto str = solver->dumps();
|
||||||
reinterpret_cast<Solver*>(s.impl_)->updateVariables();
|
const auto buf_size = str.size() + 1;
|
||||||
}
|
auto* buf = static_cast<char*>(std::malloc(buf_size));
|
||||||
|
|
||||||
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));
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
std::memcpy(buf, val.c_str(), val.size() + 1);
|
std::memcpy(buf, str.c_str(), str.size() + 1);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,38 +20,39 @@ enum KiwiErrKind {
|
|||||||
KiwiErrBadRequiredStrength,
|
KiwiErrBadRequiredStrength,
|
||||||
KiwiErrInternalSolverError,
|
KiwiErrInternalSolverError,
|
||||||
KiwiErrAlloc,
|
KiwiErrAlloc,
|
||||||
|
KiwiErrNullObject,
|
||||||
KiwiErrUnknown,
|
KiwiErrUnknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
|
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
|
||||||
|
|
||||||
typedef struct {
|
struct KiwiVarRefType;
|
||||||
void* private_;
|
struct KiwiConstraintRefType;
|
||||||
} KiwiVarRef;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiVarRefType* KiwiVarRef;
|
||||||
|
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
|
||||||
|
|
||||||
|
struct KiwiTerm {
|
||||||
KiwiVarRef var;
|
KiwiVarRef var;
|
||||||
double coefficient;
|
double coefficient;
|
||||||
} KiwiTerm;
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiExpression {
|
||||||
double constant;
|
double constant;
|
||||||
int term_count;
|
int term_count;
|
||||||
KiwiTerm terms[1];
|
struct KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?];
|
||||||
} KiwiExpression;
|
}* KiwiExpressionPtr;
|
||||||
|
|
||||||
typedef struct {
|
typedef const struct KiwiExpression* KiwiExpressionConstPtr;
|
||||||
void* private_;
|
|
||||||
} KiwiConstraintRef;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiErr {
|
||||||
enum KiwiErrKind kind;
|
enum KiwiErrKind kind;
|
||||||
char message[64];
|
const char* message;
|
||||||
} KiwiErr;
|
bool must_free;
|
||||||
|
} const* KiwiErrPtr;
|
||||||
|
|
||||||
typedef struct {
|
struct KiwiSolver;
|
||||||
void* impl_;
|
typedef struct KiwiSolver* KiwiSolverPtr;
|
||||||
} KiwiSolverRef;
|
|
||||||
|
|
||||||
KiwiVarRef kiwi_var_new(const char* name);
|
KiwiVarRef kiwi_var_new(const char* name);
|
||||||
void kiwi_var_del(KiwiVarRef var);
|
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);
|
int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
|
||||||
|
|
||||||
KiwiConstraintRef
|
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);
|
void kiwi_constraint_del(KiwiConstraintRef constraint);
|
||||||
|
|
||||||
double kiwi_constraint_strength(KiwiConstraintRef constraint);
|
double kiwi_constraint_strength(KiwiConstraintRef constraint);
|
||||||
|
|
||||||
enum KiwiRelOp kiwi_constraint_op(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(
|
KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
KiwiConstraintRef constraint,
|
KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
KiwiExpression* out,
|
bool kiwi_solver_has_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
int out_size
|
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);
|
KiwiSolverPtr kiwi_solver_new();
|
||||||
|
void kiwi_solver_del(KiwiSolverPtr sp);
|
||||||
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);
|
|
||||||
// LuaJIT end
|
// LuaJIT end
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
191
kiwi.lua
191
kiwi.lua
@@ -22,86 +22,85 @@ enum KiwiErrKind {
|
|||||||
KiwiErrBadRequiredStrength,
|
KiwiErrBadRequiredStrength,
|
||||||
KiwiErrInternalSolverError,
|
KiwiErrInternalSolverError,
|
||||||
KiwiErrAlloc,
|
KiwiErrAlloc,
|
||||||
|
KiwiErrNullObject,
|
||||||
KiwiErrUnknown,
|
KiwiErrUnknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KiwiRelOp { LE, GE, EQ };
|
enum KiwiRelOp { LE, GE, EQ };
|
||||||
|
|
||||||
typedef struct {
|
struct KiwiVarRefType;
|
||||||
void* private_;
|
struct KiwiConstraintRefType;
|
||||||
} KiwiVarRef;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiVarRefType* KiwiVarRef;
|
||||||
|
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
|
||||||
|
|
||||||
|
struct KiwiTerm {
|
||||||
KiwiVarRef var;
|
KiwiVarRef var;
|
||||||
double coefficient;
|
double coefficient;
|
||||||
} KiwiTerm;
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiExpression {
|
||||||
double constant;
|
double constant;
|
||||||
int term_count;
|
int term_count;
|
||||||
KiwiTerm terms_[?];
|
struct KiwiTerm terms_[?];
|
||||||
} KiwiExpression;
|
}* KiwiExpressionPtr;
|
||||||
|
|
||||||
typedef struct {
|
typedef const struct KiwiExpression* KiwiExpressionConstPtr;
|
||||||
void* private_;
|
|
||||||
} KiwiConstraintRef;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct KiwiErr {
|
||||||
enum KiwiErrKind kind;
|
enum KiwiErrKind kind;
|
||||||
char message[64];
|
const char* message;
|
||||||
} KiwiErr;
|
bool must_free;
|
||||||
|
} const* KiwiErrPtr;
|
||||||
|
|
||||||
typedef struct {
|
struct KiwiSolver;
|
||||||
void* impl_;
|
typedef struct KiwiSolver* KiwiSolverPtr;
|
||||||
} KiwiSolverRef;
|
|
||||||
|
|
||||||
KiwiVarRef kiwi_var_new(const char* name);
|
KiwiVarRef kiwi_var_new(const char* name);
|
||||||
void kiwi_var_del(KiwiVarRef var);
|
void kiwi_var_del(KiwiVarRef var);
|
||||||
|
|
||||||
const char* kiwi_var_name(KiwiVarRef var);
|
const char* kiwi_var_name(KiwiVarRef var);
|
||||||
|
|
||||||
void kiwi_var_set_name(KiwiVarRef var, const char* name);
|
void kiwi_var_set_name(KiwiVarRef var, const char* name);
|
||||||
|
|
||||||
double kiwi_var_value(KiwiVarRef var);
|
double kiwi_var_value(KiwiVarRef var);
|
||||||
|
|
||||||
void kiwi_var_set_value(KiwiVarRef var, double value);
|
void kiwi_var_set_value(KiwiVarRef var, double value);
|
||||||
|
|
||||||
int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
|
int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
|
||||||
|
|
||||||
KiwiConstraintRef
|
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);
|
void kiwi_constraint_del(KiwiConstraintRef constraint);
|
||||||
|
|
||||||
double kiwi_constraint_strength(KiwiConstraintRef constraint);
|
double kiwi_constraint_strength(KiwiConstraintRef constraint);
|
||||||
|
|
||||||
enum KiwiRelOp kiwi_constraint_op(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(
|
KiwiErrPtr kiwi_solver_add_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
KiwiConstraintRef constraint,
|
KiwiErrPtr kiwi_solver_remove_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
KiwiExpression* out,
|
bool kiwi_solver_has_constraint(KiwiSolverPtr sp, KiwiConstraintRef constraint);
|
||||||
int out_size
|
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);
|
KiwiSolverPtr kiwi_solver_new();
|
||||||
KiwiErr kiwi_solver_remove_constraint(KiwiSolverRef s, KiwiConstraintRef constraint);
|
void kiwi_solver_del(KiwiSolverPtr sp);
|
||||||
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);
|
|
||||||
|
|
||||||
void free(void *);
|
void free(void *);
|
||||||
]])
|
]])
|
||||||
|
|
||||||
local strformat = string.format
|
local strformat = string.format
|
||||||
local ffi_cast, ffi_copy, ffi_istype, ffi_new, ffi_sizeof, ffi_string =
|
local ffi_cast, ffi_copy, ffi_gc, ffi_istype, ffi_new, ffi_string =
|
||||||
ffi.cast, ffi.copy, ffi.istype, ffi.new, ffi.sizeof, ffi.string
|
ffi.cast, ffi.copy, ffi.gc, ffi.istype, ffi.new, ffi.string
|
||||||
|
|
||||||
local concat = table.concat
|
local concat = table.concat
|
||||||
local has_table_new, new_tab = pcall(require, "table.new")
|
local has_table_new, new_tab = pcall(require, "table.new")
|
||||||
@@ -121,13 +120,10 @@ end
|
|||||||
---| '"KiwiErrBadRequiredStrength"' # The given strength is >= required.
|
---| '"KiwiErrBadRequiredStrength"' # The given strength is >= required.
|
||||||
---| '"KiwiErrInternalSolverError"' # An internal solver error occurred.
|
---| '"KiwiErrInternalSolverError"' # An internal solver error occurred.
|
||||||
---| '"KiwiErrAlloc"' # A memory allocation error occurred.
|
---| '"KiwiErrAlloc"' # A memory allocation error occurred.
|
||||||
|
---| '"KiwiErrNullObject"' # A method was invoked on a null or empty object.
|
||||||
---| '"KiwiErrUnknown"' # An unknown error occurred.
|
---| '"KiwiErrUnknown"' # An unknown error occurred.
|
||||||
kiwi.ErrKind = ffi.typeof("enum KiwiErrKind") --[[@as kiwi.ErrKind]]
|
kiwi.ErrKind = ffi.typeof("enum KiwiErrKind") --[[@as kiwi.ErrKind]]
|
||||||
|
|
||||||
---@class kiwi.KiwiErr: ffi.ctype*
|
|
||||||
---@field package kind kiwi.ErrKind
|
|
||||||
---@field package message ffi.cdata*
|
|
||||||
|
|
||||||
---@alias kiwi.RelOp
|
---@alias kiwi.RelOp
|
||||||
---| '"LE"' # <= (less than or equal)
|
---| '"LE"' # <= (less than or equal)
|
||||||
---| '"GE"' # >= (greater than or equal)
|
---| '"GE"' # >= (greater than or equal)
|
||||||
@@ -156,18 +152,21 @@ function kiwi.Strength.create(a, b, c, w)
|
|||||||
return clamp(a * w) * 1000000.0 + clamp(b * w) * 1000.0 + clamp(c * w)
|
return clamp(a * w) * 1000000.0 + clamp(b * w) * 1000.0 + clamp(c * w)
|
||||||
end
|
end
|
||||||
|
|
||||||
local Var = ffi.typeof("KiwiVarRef") --[[@as kiwi.Var]]
|
local Var = ffi.typeof("struct KiwiVarRefType") --[[@as kiwi.Var]]
|
||||||
kiwi.Var = Var
|
kiwi.Var = Var
|
||||||
|
|
||||||
local Term = ffi.typeof("KiwiTerm") --[[@as kiwi.Term]]
|
local Term = ffi.typeof("struct KiwiTerm") --[[@as kiwi.Term]]
|
||||||
kiwi.Term = Term
|
kiwi.Term = Term
|
||||||
|
|
||||||
local Expression = ffi.typeof("KiwiExpression") --[[@as kiwi.Expression]]
|
local Expression = ffi.typeof("struct KiwiExpression") --[[@as kiwi.Expression]]
|
||||||
kiwi.Expression = Expression
|
kiwi.Expression = Expression
|
||||||
|
|
||||||
local Constraint = ffi.typeof("KiwiConstraintRef") --[[@as kiwi.Constraint]]
|
local Constraint = ffi.typeof("struct KiwiConstraintRefType") --[[@as kiwi.Constraint]]
|
||||||
kiwi.Constraint = Constraint
|
kiwi.Constraint = Constraint
|
||||||
|
|
||||||
|
-- JIT compiler NYI: bad argument type if ffi.sizeof is used with a structure member
|
||||||
|
local SIZEOF_TERM = ffi.sizeof(Term)
|
||||||
|
|
||||||
--- Define a constraint with expressions as `a <= b`.
|
--- Define a constraint with expressions as `a <= b`.
|
||||||
---@param a kiwi.Expression|kiwi.Term|kiwi.Var|number
|
---@param a kiwi.Expression|kiwi.Term|kiwi.Var|number
|
||||||
---@param b kiwi.Expression|kiwi.Term|kiwi.Var|number
|
---@param b kiwi.Expression|kiwi.Term|kiwi.Var|number
|
||||||
@@ -199,8 +198,10 @@ end
|
|||||||
---@param term kiwi.Term
|
---@param term kiwi.Term
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function add_expr_term(expr, term)
|
local function add_expr_term(expr, term)
|
||||||
local ret = ffi_new(Expression, expr.term_count + 1, expr.constant, expr.term_count + 1) --[[@as kiwi.Expression]]
|
local ret = ffi_new(Expression, expr.term_count + 1) --[[@as kiwi.Expression]]
|
||||||
ffi_copy(ret.terms_, expr.terms_, ffi_sizeof(expr.terms_, expr.term_count)) ---@diagnostic disable-line: param-type-mismatch
|
ret.constant = expr.constant
|
||||||
|
ret.term_count = expr.term_count + 1
|
||||||
|
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count) ---@diagnostic disable-line: param-type-mismatch
|
||||||
ret.terms_[expr.term_count] = term
|
ret.terms_[expr.term_count] = term
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
@@ -209,7 +210,9 @@ end
|
|||||||
---@param term kiwi.Term
|
---@param term kiwi.Term
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function new_expr_one(constant, term)
|
local function new_expr_one(constant, term)
|
||||||
local ret = ffi_new(Expression, 1, constant, 1) --[[@as kiwi.Expression]]
|
local ret = ffi_new(Expression, 1) --[[@as kiwi.Expression]]
|
||||||
|
ret.constant = constant
|
||||||
|
ret.term_count = 1
|
||||||
ret.terms_[0] = term
|
ret.terms_[0] = term
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
@@ -219,7 +222,9 @@ end
|
|||||||
---@param term2 kiwi.Term
|
---@param term2 kiwi.Term
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function new_expr_pair(constant, term1, term2)
|
local function new_expr_pair(constant, term1, term2)
|
||||||
local ret = ffi_new(Expression, 2, constant, 2) --[[@as kiwi.Expression]]
|
local ret = ffi_new(Expression, 2) --[[@as kiwi.Expression]]
|
||||||
|
ret.constant = constant
|
||||||
|
ret.term_count = 2
|
||||||
ret.terms_[0] = term1
|
ret.terms_[0] = term1
|
||||||
ret.terms_[1] = term2
|
ret.terms_[1] = term2
|
||||||
return ret
|
return ret
|
||||||
@@ -266,10 +271,9 @@ end
|
|||||||
|
|
||||||
ffi.metatype(Var, {
|
ffi.metatype(Var, {
|
||||||
__index = Var_cls,
|
__index = Var_cls,
|
||||||
__gc = ckiwi.kiwi_var_del,
|
|
||||||
|
|
||||||
__new = function(_, name)
|
__new = function(_, name)
|
||||||
return ckiwi.kiwi_var_new(name)
|
return ffi_gc(ckiwi.kiwi_var_new(name), ckiwi.kiwi_var_del)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__mul = function(a, b)
|
__mul = function(a, b)
|
||||||
@@ -393,7 +397,8 @@ ffi.metatype(Term, {
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
__tostring = function(term)
|
__tostring = function(term)
|
||||||
return tostring(term.coefficient) .. " " .. term.var:name()
|
return tostring(term.var:name())
|
||||||
|
--return tostring(term.coefficient) .. " " .. term.var:name()
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -419,8 +424,8 @@ do
|
|||||||
a.constant + b.constant,
|
a.constant + b.constant,
|
||||||
a.term_count + b.term_count
|
a.term_count + b.term_count
|
||||||
) --[[@as kiwi.Expression]]
|
) --[[@as kiwi.Expression]]
|
||||||
ffi_copy(ret.terms_, a.terms_, ffi_sizeof(a.terms_, a.term_count)) ---@diagnostic disable-line: param-type-mismatch
|
ffi_copy(ret.terms_, a.terms_, SIZEOF_TERM * a.term_count) ---@diagnostic disable-line: param-type-mismatch
|
||||||
ffi_copy(ret.terms_[a.term_count], b.terms_, ffi_sizeof(b.terms_, b.term_count)) ---@diagnostic disable-line: param-type-mismatch
|
ffi_copy(ret.terms_[a.term_count], b.terms_, SIZEOF_TERM * b.term_count) ---@diagnostic disable-line: param-type-mismatch
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -428,8 +433,11 @@ do
|
|||||||
---@param constant number
|
---@param constant number
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function new_expr_constant(expr, constant)
|
local function new_expr_constant(expr, constant)
|
||||||
local ret = ffi_new(Expression, expr.term_count, constant, expr.term_count) --[[@as kiwi.Expression]]
|
local ret = ffi_new(Expression, expr.term_count) --[[@as kiwi.Expression]]
|
||||||
ffi_copy(ret.terms_, expr.terms_, ffi_sizeof(expr.terms_, expr.term_count)) ---@diagnostic disable-line: param-type-mismatch
|
ret.constant = constant
|
||||||
|
ret.term_count = expr.term_count
|
||||||
|
|
||||||
|
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count) ---@diagnostic disable-line: param-type-mismatch
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -577,6 +585,7 @@ end
|
|||||||
---@return kiwi.Constraint
|
---@return kiwi.Constraint
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function kiwi.new_pair_ratio_constraint(left, coeff, right, constant, op, strength)
|
function kiwi.new_pair_ratio_constraint(left, coeff, right, constant, op, strength)
|
||||||
|
assert(ffi_istype(Var, left) and ffi_istype(Var, right))
|
||||||
return Constraint(
|
return Constraint(
|
||||||
new_expr_pair(-(constant or 0.0), Term(left), Term(right, -coeff)),
|
new_expr_pair(-(constant or 0.0), Term(left), Term(right, -coeff)),
|
||||||
op,
|
op,
|
||||||
@@ -594,6 +603,7 @@ end
|
|||||||
---@return kiwi.Constraint
|
---@return kiwi.Constraint
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function kiwi.new_pair_constraint(left, right, constant, op, strength)
|
function kiwi.new_pair_constraint(left, right, constant, op, strength)
|
||||||
|
assert(ffi_istype(Var, left) and ffi_istype(Var, right))
|
||||||
return Constraint(
|
return Constraint(
|
||||||
new_expr_pair(-(constant or 0.0), Term(left), Term(right, -1.0)),
|
new_expr_pair(-(constant or 0.0), Term(left), Term(right, -1.0)),
|
||||||
op,
|
op,
|
||||||
@@ -610,6 +620,7 @@ end
|
|||||||
---@return kiwi.Constraint
|
---@return kiwi.Constraint
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function kiwi.new_single_constraint(var, constant, op, strength)
|
function kiwi.new_single_constraint(var, constant, op, strength)
|
||||||
|
assert(ffi_istype(Var, var))
|
||||||
return Constraint(new_expr_one(-(constant or 0.0), Term(var)), op, strength)
|
return Constraint(new_expr_one(-(constant or 0.0), Term(var)), op, strength)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -618,10 +629,12 @@ local REQUIRED = Strength.REQUIRED
|
|||||||
|
|
||||||
ffi.metatype(Constraint, {
|
ffi.metatype(Constraint, {
|
||||||
__index = Constraint_cls,
|
__index = Constraint_cls,
|
||||||
__gc = ckiwi.kiwi_constraint_del,
|
|
||||||
|
|
||||||
__new = function(_, expr, op, strength)
|
__new = function(_, expr, op, strength)
|
||||||
return ckiwi.kiwi_constraint_new(expr, op or "EQ", strength or REQUIRED)
|
return ffi_gc(
|
||||||
|
ckiwi.kiwi_constraint_new(expr, op or "EQ", strength or REQUIRED),
|
||||||
|
ckiwi.kiwi_constraint_del
|
||||||
|
)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
@@ -670,19 +683,42 @@ local function new_error(kind, message, solver, item)
|
|||||||
}, Error_mt)
|
}, Error_mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param f fun(solver: kiwi.Solver, item: any, ...): kiwi.KiwiErr
|
local C = ffi.C
|
||||||
|
|
||||||
|
---@class kiwi.KiwiErr: ffi.ctype*
|
||||||
|
---@field package kind kiwi.ErrKind
|
||||||
|
---@field package message ffi.cdata*
|
||||||
|
---@field package must_free boolean
|
||||||
|
---@overload fun(): kiwi.KiwiErr
|
||||||
|
local KiwiErr = ffi.typeof("struct KiwiErr") --[[@as kiwi.KiwiErr]]
|
||||||
|
--
|
||||||
|
---@param f fun(solver: kiwi.Solver, item: any, ...): kiwi.KiwiErr?
|
||||||
---@param solver kiwi.Solver
|
---@param solver kiwi.Solver
|
||||||
---@param item any
|
---@param item any
|
||||||
local function try_solver(f, solver, item, ...)
|
local function try_solver(f, solver, item, ...)
|
||||||
local err = f(solver, item, ...)
|
local err = f(solver, item, ...)
|
||||||
if err.kind ~= 0 then
|
if err ~= nil then
|
||||||
error(new_error(err.kind, ffi_string(err.message), solver, item))
|
local kind = err.kind
|
||||||
|
local message = err.message ~= nil and ffi_string(err.message) or ""
|
||||||
|
if err.must_free then
|
||||||
|
print("FEEE")
|
||||||
|
C.free(err)
|
||||||
|
end
|
||||||
|
error(new_error(kind, message, solver, item))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class kiwi.Solver: ffi.ctype*
|
---@class kiwi.Solver: ffi.ctype*
|
||||||
---@overload fun(): kiwi.Solver
|
---@overload fun(): kiwi.Solver
|
||||||
local Solver_cls = {
|
local Solver_cls = {
|
||||||
|
--- Test whether a constraint is in the solver.
|
||||||
|
---@type fun(self: kiwi.Solver, constraint: kiwi.Constraint): boolean
|
||||||
|
has_constraint = ckiwi.kiwi_solver_has_constraint,
|
||||||
|
|
||||||
|
--- Test whether an edit variable has been added to the solver.
|
||||||
|
---@type fun(self: kiwi.Solver, var: kiwi.Var): boolean
|
||||||
|
has_edit_var = ckiwi.kiwi_solver_has_edit_var,
|
||||||
|
|
||||||
--- Update the values of the external solver variables.
|
--- Update the values of the external solver variables.
|
||||||
---@type fun(self: kiwi.Solver)
|
---@type fun(self: kiwi.Solver)
|
||||||
update_vars = ckiwi.kiwi_solver_update_vars,
|
update_vars = ckiwi.kiwi_solver_update_vars,
|
||||||
@@ -719,14 +755,6 @@ function Solver_cls:remove_constraint(constraint)
|
|||||||
try_solver(ckiwi.kiwi_solver_remove_constraint, self, constraint)
|
try_solver(ckiwi.kiwi_solver_remove_constraint, self, constraint)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test whether a constraint is in the solver.
|
|
||||||
---@param constraint kiwi.Constraint
|
|
||||||
---@return boolean
|
|
||||||
---@nodiscard
|
|
||||||
function Solver_cls:has_constraint(constraint)
|
|
||||||
return ckiwi.kiwi_solver_has_constraint(self, constraint) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Adds an edit variable to the solver.
|
--- Adds an edit variable to the solver.
|
||||||
---
|
---
|
||||||
--- This method should be called before the `suggestValue` method is
|
--- This method should be called before the `suggestValue` method is
|
||||||
@@ -748,14 +776,6 @@ function Solver_cls:remove_edit_var(var)
|
|||||||
try_solver(ckiwi.kiwi_solver_remove_edit_var, self, var)
|
try_solver(ckiwi.kiwi_solver_remove_edit_var, self, var)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test whether an edit variable has been added to the solver.
|
|
||||||
---@param var kiwi.Var the edit variable to check
|
|
||||||
---@return boolean
|
|
||||||
---@nodiscard
|
|
||||||
function Solver_cls:has_edit_var(var)
|
|
||||||
return ckiwi.kiwi_solver_has_edit_var(self, var) ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Suggest a value for the given edit variable.
|
--- Suggest a value for the given edit variable.
|
||||||
--- This method should be used after an edit variable has been added to the solver in order
|
--- This method should be used after an edit variable has been added to the solver in order
|
||||||
--- to suggest the value for that variable. After all suggestions have been made,
|
--- to suggest the value for that variable. After all suggestions have been made,
|
||||||
@@ -772,18 +792,17 @@ end
|
|||||||
---@return string
|
---@return string
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function Solver_cls:dumps()
|
function Solver_cls:dumps()
|
||||||
local cs = ckiwi.kiwi_solver_dumps(self, nil)
|
local cs = ckiwi.kiwi_solver_dumps(self)
|
||||||
local s = ffi_string(cs)
|
local s = ffi_string(cs)
|
||||||
ffi.C.free(cs)
|
C.free(cs)
|
||||||
return s
|
return s
|
||||||
end
|
end
|
||||||
|
|
||||||
kiwi.Solver = ffi.metatype("KiwiSolverRef", {
|
kiwi.Solver = ffi.metatype("struct KiwiSolver", {
|
||||||
__index = Solver_cls,
|
__index = Solver_cls,
|
||||||
__new = function(_)
|
__new = function(_)
|
||||||
return ckiwi.kiwi_solver_new()
|
return ffi_gc(ckiwi.kiwi_solver_new(), ckiwi.kiwi_solver_del)
|
||||||
end,
|
end,
|
||||||
__gc = ckiwi.kiwi_solver_del,
|
|
||||||
|
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
return strformat("kiwi.Solver(0x%X)", ffi_cast("intptr_t", ffi_cast("void*", self)))
|
return strformat("kiwi.Solver(0x%X)", ffi_cast("intptr_t", ffi_cast("void*", self)))
|
||||||
|
|||||||
@@ -29,5 +29,6 @@ build = {
|
|||||||
install_variables = {
|
install_variables = {
|
||||||
INST_LIBDIR = "$(LIBDIR)",
|
INST_LIBDIR = "$(LIBDIR)",
|
||||||
INST_LUADIR = "$(LUADIR)",
|
INST_LUADIR = "$(LUADIR)",
|
||||||
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user