6 Commits

Author SHA1 Message Date
f6bf09a3eb huge 2024-02-24 02:43:11 -06:00
579d671a77 Update rockspec 2024-02-23 13:07:02 -06:00
61ba76c5a3 Cleanup 2024-02-23 12:56:04 -06:00
8854c0edbe add build.bat 2024-02-22 23:23:07 -06:00
00a9fda814 again 2024-02-22 22:57:27 -06:00
a8c1a10cab fix MSVC warnings 2024-02-22 22:40:53 -06:00
15 changed files with 832 additions and 821 deletions

View File

@@ -65,7 +65,7 @@ PointerAlignment: Left
ReferenceAlignment: Left # New in v13. int &name ==> int& name ReferenceAlignment: Left # New in v13. int &name ==> int& name
ReflowComments: false ReflowComments: false
SeparateDefinitionBlocks: Always # New in v14. SeparateDefinitionBlocks: Always # New in v14.
SortIncludes: true SortIncludes: false
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false SpaceAfterLogicalNot: false

2
.gitignore vendored
View File

@@ -2,6 +2,8 @@
/lua /lua
/lua_modules /lua_modules
/.luarocks /.luarocks
*.pch
*.gch
*.so *.so
*.o *.o
.cache/ .cache/

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "Lua 5.4", "runtime.version": "LuaJIT",
"runtime.path": [ "runtime.path": [
"./?/init.lua", "./?/init.lua",
"./?.lua", "./?.lua",

View File

@@ -1,56 +1,85 @@
SRCDIR := .
CC := $(CROSS)gcc CC := $(CROSS)gcc
CFLAGS := -fPIC -Os CP := cp
CXXFLAGS := -I$(SRCDIR)/kiwi -fno-rtti RM := rm
F_LTO := -flto=auto
CXXFLAGS_EXTRA := -pedantic -std=c++14 -Wall $(F_LTO)
CFLAGS_EXTRA := -pedantic -std=c99 -Wall $(F_LTO)
LIBFLAG := -shared LIBFLAG := -shared
LIB_EXT := so LIB_EXT := so
LUA_INCDIR := /usr/include LUA_INCDIR := /usr/include
ifeq ($(findstring gcc,$(CC)),gcc) SRCDIR := .
CXX := $(subst gcc,g++,$(CC))
CXXFLAGS += -std=c++14 OPTFLAG := -O2
ifneq ($(SANITIZE),) CCFLAGS += $(OPTFLAG) -fPIC -Wall -fstrict-flex-arrays -fvisibility=hidden -Wformat=2 -Wconversion -Wimplicit-fallthrough
CFLAGS += -fsanitize=undefined -fsanitize=address
endif SANITIZE_FLAGS := -fsanitize=undefined -fsanitize=address
LTO_FLAGS := -flto=auto
ifdef SANITIZE
CCFLAGS += $(SANITIZE_FLAGS)
endif
ifdef LTO
CCFLAGS += $(LTO_FLAGS)
endif
override CPPFLAGS += -I$(SRCDIR) -I$(SRCDIR)/kiwi -I$(LUA_INCDIR)
override CXXFLAGS += -std=c++14 -fno-rtti $(CCFLAGS)
override CFLAGS += -std=c99 $(CCFLAGS)
ifneq ($(filter %gcc,$(CC)),)
CXX := $(patsubst %gcc,%g++,$(CC))
PCH := ljkiwi.hpp.gch
else else
ifeq ($(CC),clang) ifneq ($(filter %clang,$(CC)),)
CXX := clang++ CXX := $(patsubst %clang,%clang++,$(CC))
CXXFLAGS += -std=c++14 override CXXFLAGS += -pedantic -Wno-c99-extensions
ifneq ($(SANITIZE),) PCH := ljkiwi.hpp.pch
CFLAGS += -fsanitize=undefined -fsanitize=address
endif
else
CXX := $(CC)
endif endif
endif endif
OBJS := ckiwi.o luakiwi.o ifdef LUA
LUA_VERSION ?= $(lastword $(shell $(LUA) -e "print(_VERSION)"))
endif
VPATH = $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi ifndef LUA_VERSION
LJKIWI_CKIWI := 1
else
ifneq ($(LUA_VERSION),5.1)
LJKIWI_CKIWI :=
endif
endif
all: ckiwi.$(LIB_EXT) OBJS := luakiwi.o
ifdef LJKIWI_CKIWI
OBJS += ckiwi.o
endif
vpath %.cpp $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
vpath %.h $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
all: ljkiwi.$(LIB_EXT)
install: install:
cp -f ckiwi.$(LIB_EXT) $(INST_LIBDIR)/ckiwi.$(LIB_EXT) $(CP) -f ljkiwi.$(LIB_EXT) $(INST_LIBDIR)/ljkiwi.$(LIB_EXT)
cp -f kiwi.lua $(INST_LUADIR)/kiwi.lua $(CP) -f kiwi.lua $(INST_LUADIR)/kiwi.lua
clean: clean:
rm -f ckiwi.$(LIB_EXT) $(OBJS) $(RM) -f ljkiwi.$(LIB_EXT) $(OBJS) $(PCH)
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h
luakiwi.o: $(PCH) luakiwi-int.h luacompat.h
ckiwi.$(LIB_EXT): $(OBJS) ljkiwi.$(LIB_EXT): $(OBJS)
$(CXX) $(CXXFLAGS) $(CFLAGS) $(CFLAGS_EXTRA) -I$(SRCDIR)/kiwi $(LIBFLAG) -o $@ $^ $(CXX) $(CCFLAGS) $(LIBFLAG) -o $@ $(OBJS)
ckiwi.o: ckiwi.h %.hpp.gch: %.hpp
luakiwi.o: ckiwi.h luakiwi-int.h luacompat.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
%.hpp.pch: %.hpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
%.o: %.c %.o: %.c
$(CC) -I$(LUA_INCDIR) $(CFLAGS) $(CFLAGS_EXTRA) -c -o $@ $< $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o: %.cpp %.o: %.cpp
$(CXX) -I$(LUA_INCDIR) $(CXXFLAGS) $(CFLAGS) $(CXXFLAGS_EXTRA) -c -o $@ $< $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
.PHONY: all install clean .PHONY: all install clean

2
build.bat Normal file
View File

@@ -0,0 +1,2 @@
cl /nologo /O2 /W4 /wd4200 /c /D_CRT_SECURE_NO_DEPRECATE /I "C:\Program Files\luarocks\include" /EHs /I kiwi luakiwi\luakiwi.cpp
link /DLL /out:ckiwi.dll /def:luakiwi.def /LIBPATH:"C:\Program Files\luarocks\lib" luakiwi.obj lua54.lib

View File

@@ -1,3 +1,4 @@
#include "ljkiwi.hpp"
#include "ckiwi.h" #include "ckiwi.h"
#include <kiwi/kiwi.h> #include <kiwi/kiwi.h>
@@ -5,99 +6,17 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
using namespace kiwi; #if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
#define lk_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define lk_likely(x) (x)
#define lk_unlikely(x) (x)
#endif
namespace { namespace {
template<typename T, typename R, typename... Args> using namespace kiwi;
inline R make_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) make_var_cref(Args&&... args) {
return make_cref<Variable, KiwiVarRef>(std::forward<Args>(args)...);
}
template<typename... Args>
inline decltype(auto) make_constraint_cref(Args&&... args) {
return make_cref<Constraint, KiwiConstraintRef>(std::forward<Args>(args)...);
}
template<class T, class R>
class SharedRef {
private:
R& cref_;
public:
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"
);
R clone() const {
return make_cref<T, R>(cref());
}
void release() {
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 ptr();
}
const T* operator->() const {
return const_ptr();
}
operator const T&() const {
return cref();
}
explicit operator bool() const {
return cref_;
}
};
using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
using VariableRef = SharedRef<Variable, KiwiVarRef>;
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) { const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
if (!std::strcmp(ex.what(), base->message)) if (!std::strcmp(ex.what(), base->message))
@@ -117,18 +36,15 @@ const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
static const constexpr KiwiErr kKiwiErrUnhandledCxxException { static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown, KiwiErrUnknown,
"An unhandled C++ exception occurred." "An unhandled C++ exception occurred."};
};
static const constexpr KiwiErr kKiwiErrNullObjectArg0 { static const constexpr KiwiErr kKiwiErrNullObjectArg0 {
KiwiErrNullObject, KiwiErrNullObject,
"null object passed as argument #0 (self)" "null object passed as argument #0 (self)"};
};
static const constexpr KiwiErr kKiwiErrNullObjectArg1 { static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
KiwiErrNullObject, KiwiErrNullObject,
"null object passed as argument #1" "null object passed as argument #1"};
};
template<typename F> template<typename F>
inline const KiwiErr* wrap_err(F&& f) { inline const KiwiErr* wrap_err(F&& f) {
@@ -137,49 +53,42 @@ inline const KiwiErr* wrap_err(F&& f) {
} catch (const UnsatisfiableConstraint& ex) { } catch (const UnsatisfiableConstraint& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnsatisfiableConstraint, KiwiErrUnsatisfiableConstraint,
"The constraint cannot be satisfied." "The constraint cannot be satisfied."};
};
return &err; return &err;
} catch (const UnknownConstraint& ex) { } catch (const UnknownConstraint& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnknownConstraint, KiwiErrUnknownConstraint,
"The constraint has not been added to the solver." "The constraint has not been added to the solver."};
};
return &err; return &err;
} catch (const DuplicateConstraint& ex) { } catch (const DuplicateConstraint& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrDuplicateConstraint, KiwiErrDuplicateConstraint,
"The constraint has already been added to the solver." "The constraint has already been added to the solver."};
};
return &err; return &err;
} catch (const UnknownEditVariable& ex) { } catch (const UnknownEditVariable& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnknownEditVariable, KiwiErrUnknownEditVariable,
"The edit variable has not been added to the solver." "The edit variable has not been added to the solver."};
};
return &err; return &err;
} catch (const DuplicateEditVariable& ex) { } catch (const DuplicateEditVariable& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrDuplicateEditVariable, KiwiErrDuplicateEditVariable,
"The edit variable has already been added to the solver." "The edit variable has already been added to the solver."};
};
return &err; return &err;
} catch (const BadRequiredStrength& ex) { } catch (const BadRequiredStrength& ex) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrBadRequiredStrength, KiwiErrBadRequiredStrength,
"A required strength cannot be used in this context." "A required strength cannot be used in this context."};
};
return &err; return &err;
} catch (const InternalSolverError& ex) { } catch (const InternalSolverError& ex) {
static const constexpr KiwiErr base { static const constexpr KiwiErr base {
KiwiErrInternalSolverError, KiwiErrInternalSolverError,
"An internal solver error occurred." "An internal solver error occurred."};
};
return new_error(&base, ex); return new_error(&base, ex);
} catch (std::bad_alloc&) { } catch (std::bad_alloc&) {
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."}; static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
@@ -193,82 +102,96 @@ inline const KiwiErr* wrap_err(F&& f) {
} }
template<typename P, typename R, typename F> template<typename P, typename R, typename F>
inline const KiwiErr* wrap_err(P ptr, F&& f) { inline const KiwiErr* wrap_err(P self, F&& f) {
if (!ptr) { if (lk_unlikely(!self)) {
return &kKiwiErrNullObjectArg0; return &kKiwiErrNullObjectArg0;
} }
return wrap_err([&]() { f(ptr); }); return wrap_err([&]() { f(self->solver); });
} }
template<typename P, typename R, typename F> template<typename P, typename R, typename F>
inline const KiwiErr* wrap_err(P ptr, R ref, F&& f) { inline const KiwiErr* wrap_err(P self, R item, F&& f) {
if (!ptr) { if (lk_unlikely(!self)) {
return &kKiwiErrNullObjectArg0; return &kKiwiErrNullObjectArg0;
} else if (!ref) { } else if (lk_unlikely(!item)) {
return &kKiwiErrNullObjectArg1; return &kKiwiErrNullObjectArg1;
} }
return wrap_err([&]() { f(ptr, ref); }); return wrap_err([&]() { f(self->solver, *item); });
} }
} // namespace } // namespace
extern "C" { extern "C" {
KiwiVarRef kiwi_var_new(const char* name) { KiwiTypeInfo kiwi_ti_KiwiVar = {sizeof(KiwiVar), alignof(KiwiVar)};
return make_var_cref(name ? name : ""); KiwiTypeInfo kiwi_ti_KiwiConstraint = {sizeof(KiwiConstraint), alignof(KiwiConstraint)};
void kiwi_var_construct(const char* name, void* mem) {
new (mem) KiwiVar {lk_likely(name) ? name : ""};
} }
void kiwi_var_del(KiwiVarRef var) { void kiwi_var_release(KiwiVar* var) {
VariableRef(var).release(); if (lk_likely(var))
var->~KiwiVar();
} }
KiwiVarRef kiwi_var_clone(KiwiVarRef var) { void kiwi_var_retain(KiwiVar* var) {
return VariableRef(var).clone(); if (lk_likely(var)) {
} alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
new (buf) KiwiVar(*var);
const char* kiwi_var_name(KiwiVarRef var) {
const VariableRef self(var);
return self ? self->name().c_str() : "(<null>)";
}
void kiwi_var_set_name(KiwiVarRef var, const char* name) {
VariableRef self(var);
if (self)
self->setName(name);
}
double kiwi_var_value(KiwiVarRef var) {
const VariableRef self(var);
return self ? self->value() : std::numeric_limits<double>::quiet_NaN();
}
void kiwi_var_set_value(KiwiVarRef var, double value) {
VariableRef self(var);
if (self)
self->setValue(value);
}
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) {
ConstVariableRef self(var); // const defect in upstream
const VariableRef other_ref(other);
return self && other_ref && self->equals(other_ref);
}
void kiwi_expression_del_vars(KiwiExpression* expr) {
if (!expr)
return;
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
VariableRef(t->var).release();
} }
} }
KiwiConstraintRef kiwi_constraint_new( const char* kiwi_var_name(const KiwiVar* var) {
return lk_likely(var) ? var->name().c_str() : "(<null>)";
}
void kiwi_var_set_name(KiwiVar* var, const char* name) {
if (lk_likely(var))
var->setName(name);
}
double kiwi_var_value(const KiwiVar* var) {
return lk_likely(var) ? var->value() : std::numeric_limits<double>::quiet_NaN();
}
void kiwi_var_set_value(KiwiVar* var, double value) {
if (lk_likely(var))
var->setValue(value);
}
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other) {
return lk_likely(var && other) && var->equals(*other);
}
void kiwi_expression_retain(KiwiExpression* expr) {
if (lk_unlikely(!expr))
return;
alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
new (buf) KiwiVar(*t->var);
}
}
void kiwi_expression_destroy(KiwiExpression* expr) {
if (lk_unlikely(!expr))
return;
if (expr->owner) {
expr->owner->~KiwiConstraint();
} else {
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
t->var->~KiwiVar();
}
}
}
void kiwi_constraint_construct(
const KiwiExpression* lhs, const KiwiExpression* lhs,
const KiwiExpression* rhs, const KiwiExpression* rhs,
enum KiwiRelOp op, enum KiwiRelOp op,
double strength double strength,
void* mem
) { ) {
if (strength < 0.0) { if (strength < 0.0) {
strength = kiwi::strength::required; strength = kiwi::strength::required;
@@ -278,69 +201,69 @@ KiwiConstraintRef kiwi_constraint_new(
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0)); terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
if (lhs) { if (lhs) {
for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) { for (auto* t = lhs->terms_; t != lhs->terms_ + lhs->term_count; ++t) {
ConstVariableRef var(t->var); if (t->var)
if (var) terms.emplace_back(*t->var, t->coefficient);
terms.emplace_back(var.cref(), t->coefficient);
} }
} }
if (rhs) { if (rhs) {
for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) { for (auto* t = rhs->terms_; t != rhs->terms_ + rhs->term_count; ++t) {
ConstVariableRef var(t->var); if (t->var)
if (var) terms.emplace_back(*t->var, -t->coefficient);
terms.emplace_back(var.cref(), -t->coefficient);
} }
} }
return make_constraint_cref( new (mem) Constraint(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)), Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op), static_cast<RelationalOperator>(op),
strength strength
); );
} }
void kiwi_constraint_del(KiwiConstraintRef constraint) { void kiwi_constraint_release(KiwiConstraint* c) {
ConstraintRef(constraint).release(); if (lk_likely(c))
c->~KiwiConstraint();
} }
KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint) { void kiwi_constraint_retain(KiwiConstraint* c) {
return ConstraintRef(constraint).clone(); if (lk_likely(c)) {
} alignas(KiwiConstraint) unsigned char buf[sizeof(KiwiConstraint)];
new (buf) KiwiConstraint(*c);
double kiwi_constraint_strength(KiwiConstraintRef constraint) {
const ConstraintRef self(constraint);
return self ? self->strength() : std::numeric_limits<double>::quiet_NaN();
}
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint) {
const ConstraintRef self(constraint);
return self ? static_cast<KiwiRelOp>(self->op()) : KiwiRelOp::KIWI_OP_EQ;
}
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 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)
return n_terms;
auto* p = out->terms;
for (const auto& t : terms) {
*p = KiwiTerm {make_var_cref(t.variable()), t.coefficient()};
++p;
} }
out->term_count = p - out->terms; }
out->constant = expr.constant();
return n_terms; double kiwi_constraint_strength(const KiwiConstraint* c) {
return lk_likely(c) ? c->strength() : std::numeric_limits<double>::quiet_NaN();
}
enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c) {
return lk_likely(c) ? static_cast<KiwiRelOp>(c->op()) : KiwiRelOp::KIWI_OP_EQ;
}
bool kiwi_constraint_violated(const KiwiConstraint* c) {
return lk_likely(c) ? c->violated() : false;
}
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size) {
if (lk_unlikely(!c))
return 0;
const auto& expr = c->expression();
const auto& terms = expr.terms();
const auto term_count = static_cast<int>(terms.size());
if (!out || out_size < term_count)
return term_count;
for (int i = 0; i < term_count; ++i) {
out->terms_[i].var = const_cast<KiwiVar*>(&terms[i].variable());
out->terms_[i].coefficient = terms[i].coefficient();
}
out->constant = expr.constant();
out->term_count = term_count;
out->owner = c;
kiwi_constraint_retain(c);
return term_count;
} }
struct KiwiSolver { struct KiwiSolver {
@@ -348,79 +271,80 @@ struct KiwiSolver {
Solver solver; Solver solver;
}; };
KiwiSolver* kiwi_solver_new(unsigned error_mask) { KiwiTypeInfo kiwi_ti_KiwiSolver = {sizeof(KiwiSolver), alignof(KiwiSolver)};
return new KiwiSolver {error_mask};
void kiwi_solver_construct(unsigned error_mask, void* mem) {
new (mem) KiwiSolver {error_mask};
} }
void kiwi_solver_del(KiwiSolver* s) { void kiwi_solver_destroy(KiwiSolver* s) {
if (s) if (lk_likely(s))
delete s; s->~KiwiSolver();
} }
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { unsigned kiwi_solver_get_error_mask(const KiwiSolver* s) {
return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) { return lk_likely(s) ? s->error_mask : 0;
s->solver.addConstraint(c);
});
} }
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask) {
return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) { if (lk_likely(s))
s->solver.removeConstraint(c); s->error_mask = mask;
});
} }
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraintRef constraint) { const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
ConstraintRef c(constraint); return wrap_err(s, constraint, [](auto& s, const auto& c) { s.addConstraint(c); });
if (!s || !c) }
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
return wrap_err(s, constraint, [](auto& s, const auto& c) { s.removeConstraint(c); });
}
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint) {
if (lk_unlikely(!s || !constraint))
return 0; return 0;
return s->solver.hasConstraint(c); return s->solver.hasConstraint(*constraint);
} }
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVarRef var, double strength) { const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength) {
return wrap_err(s, VariableRef(var), [strength](auto* s, const auto v) { return wrap_err(s, var, [strength](auto& s, const auto& v) {
s->solver.addEditVariable(v, strength); s.addEditVariable(v, strength);
}); });
} }
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVarRef var) { const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var) {
return wrap_err(s, VariableRef(var), [](auto* s, const auto v) { return wrap_err(s, var, [](auto& s, const auto& v) { s.removeEditVariable(v); });
s->solver.removeEditVariable(v);
});
} }
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVarRef var) { bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var) {
VariableRef v(var); if (lk_unlikely(!s || !var))
if (!s || !v)
return 0; return 0;
return s->solver.hasEditVariable(v); return s->solver.hasEditVariable(*var);
} }
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVarRef var, double value) { const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value) {
return wrap_err(s, VariableRef(var), [value](auto* s, const auto v) { return wrap_err(s, var, [value](auto& s, const auto& v) { s.suggestValue(v, value); });
s->solver.suggestValue(v, value);
});
} }
void kiwi_solver_update_vars(KiwiSolver* s) { void kiwi_solver_update_vars(KiwiSolver* s) {
if (s) if (lk_likely(s))
s->solver.updateVariables(); s->solver.updateVariables();
} }
void kiwi_solver_reset(KiwiSolver* s) { void kiwi_solver_reset(KiwiSolver* s) {
if (s) if (lk_likely(s))
s->solver.reset(); s->solver.reset();
} }
void kiwi_solver_dump(const KiwiSolver* s) { void kiwi_solver_dump(const KiwiSolver* s) {
if (s) if (lk_likely(s))
s->solver.dump(); s->solver.dump();
} }
char* kiwi_solver_dumps(const KiwiSolver* s) { char* kiwi_solver_dumps(const KiwiSolver* s) {
if (!s) if (lk_unlikely(!s))
return nullptr; return nullptr;
const auto str = s->solver.dumps(); // upstream library defect const auto& str = s->solver.dumps();
const auto buf_size = str.size() + 1; const auto buf_size = str.size() + 1;
auto* buf = static_cast<char*>(std::malloc(buf_size)); auto* buf = static_cast<char*>(std::malloc(buf_size));
if (!buf) if (!buf)

View File

@@ -1,13 +1,38 @@
#ifndef KIWI_CKIWI_H_ #ifndef LJKIWI_CKIWI_H_
#define KIWI_CKIWI_H_ #define LJKIWI_CKIWI_H_
#ifdef __cplusplus #if !defined(_MSC_VER) || _MSC_VER >= 1900
extern "C" { #undef LJKIWI_USE_FAM_1
#else #else
#include <stdbool.h> #define LJKIWI_USE_FAM_1
#endif #endif
#define KIWI_REF_ISNULL(ref) ((ref).impl_ == NULL) #ifdef __cplusplus
namespace kiwi {
class Variable;
class Constraint;
} // namespace kiwi
extern "C" {
typedef kiwi::Variable KiwiVar;
typedef kiwi::Constraint KiwiConstraint;
#else
typedef struct KiwiVar KiwiVar;
typedef struct KiwiConstraint KiwiConstraint;
#endif
typedef struct KiwiTypeInfo {
unsigned size;
unsigned align;
} KiwiTypeInfo;
#if __GNUC__
#pragma GCC visibility push(default)
#endif
extern KiwiTypeInfo kiwi_ti_KiwiVar, kiwi_ti_KiwiConstraint, kiwi_ti_KiwiSolver;
// LuaJIT start // LuaJIT start
enum KiwiErrKind { enum KiwiErrKind {
@@ -26,18 +51,24 @@ enum KiwiErrKind {
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ }; enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
typedef struct KiwiVarRefType* KiwiVarRef;
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
typedef struct KiwiTerm { typedef struct KiwiTerm {
KiwiVarRef var; KiwiVar* var;
double coefficient; double coefficient;
} KiwiTerm; } KiwiTerm;
typedef struct KiwiExpression { typedef struct KiwiExpression {
double constant; double constant;
int term_count; int term_count;
KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?]; KiwiConstraint* owner;
#if defined(LJKIWI_LUAJIT_DEF)
KiwiTerm terms_[?];
#elif defined(LJKIWI_USE_FAM_1)
KiwiTerm terms_[1]; // LuaJIT: struct KiwiTerm terms_[?];
#else
KiwiTerm terms_[];
#endif
} KiwiExpression; } KiwiExpression;
typedef struct KiwiErr { typedef struct KiwiErr {
@@ -46,56 +77,63 @@ typedef struct KiwiErr {
bool must_free; bool must_free;
} KiwiErr; } KiwiErr;
typedef struct KiwiSolver struct KiwiSolver;
KiwiSolver; // LuaJIT: typedef struct { unsigned error_mask; } KiwiSolver;
KiwiVarRef kiwi_var_new(const char* name);
void kiwi_var_del(KiwiVarRef var);
KiwiVarRef kiwi_var_clone(KiwiVarRef var);
const char* kiwi_var_name(KiwiVarRef var); void kiwi_var_construct(const char* name, void* mem);
void kiwi_var_set_name(KiwiVarRef var, const char* name); void kiwi_var_release(KiwiVar* var);
double kiwi_var_value(KiwiVarRef var); void kiwi_var_retain(KiwiVar* var);
void kiwi_var_set_value(KiwiVarRef var, double value);
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other);
void kiwi_expression_del_vars(KiwiExpression* expr); const char* kiwi_var_name(const KiwiVar* var);
void kiwi_var_set_name(KiwiVar* var, const char* name);
double kiwi_var_value(const KiwiVar* var);
void kiwi_var_set_value(KiwiVar* var, double value);
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other);
KiwiConstraintRef kiwi_constraint_new( void kiwi_expression_retain(KiwiExpression* expr);
void kiwi_expression_destroy(KiwiExpression* expr);
void kiwi_constraint_construct(
const KiwiExpression* lhs, const KiwiExpression* lhs,
const KiwiExpression* rhs, const KiwiExpression* rhs,
enum KiwiRelOp op, enum KiwiRelOp op,
double strength double strength,
void* mem
); );
void kiwi_constraint_del(KiwiConstraintRef constraint); void kiwi_constraint_release(KiwiConstraint* c);
KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint); void kiwi_constraint_retain(KiwiConstraint* c);
double kiwi_constraint_strength(KiwiConstraintRef constraint); double kiwi_constraint_strength(const KiwiConstraint* c);
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint); enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
bool kiwi_constraint_violated(KiwiConstraintRef constraint); bool kiwi_constraint_violated(const KiwiConstraint* c);
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size); int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
KiwiSolver* kiwi_solver_new(unsigned error_mask); void kiwi_solver_construct(unsigned error_mask, void* mem);
void kiwi_solver_del(KiwiSolver* s); void kiwi_solver_destroy(KiwiSolver* s);
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
bool kiwi_solver_has_constraint(const KiwiSolver* sp, KiwiConstraintRef constraint); bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* sp, KiwiVarRef var, double strength); const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* sp, KiwiVarRef var); const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
bool kiwi_solver_has_edit_var(const KiwiSolver* sp, KiwiVarRef var); bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* sp, KiwiVarRef var, double value); const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
void kiwi_solver_update_vars(KiwiSolver* sp); void kiwi_solver_update_vars(KiwiSolver* sp);
void kiwi_solver_reset(KiwiSolver* sp); void kiwi_solver_reset(KiwiSolver* sp);
void kiwi_solver_dump(const KiwiSolver* sp); void kiwi_solver_dump(const KiwiSolver* sp);
char* kiwi_solver_dumps(const KiwiSolver* sp); char* kiwi_solver_dumps(const KiwiSolver* sp);
// LuaJIT end // LuaJIT end
#if __GNUC__
#pragma GCC visibility pop
#endif
#ifdef __cplusplus #ifdef __cplusplus
} } // extern "C"
#endif #endif
// Local Variables: // Local Variables:
// mode: c++ // mode: c++
// End: // End:
#endif // KIWI_CKIWI_H_ #endif // LJKIWI_CKIWI_H_

View File

@@ -1,15 +1,15 @@
rockspec_format = "3.0" rockspec_format = "3.0"
package = "ljkiwi" package = "kiwi"
version = "scm-1" version = "scm-1"
source = { source = {
url = "git+https://github.com/jkl1337/ljkiwi", url = "git+https://github.com/jkl1337/ljkiwi",
} }
description = { description = {
summary = "A LuaJIT FFI binding for the Kiwi constraint solver.", summary = "LuaJIT FFI and Lua binding for the Kiwi constraint solver.",
detailed = [[ detailed = [[
ljkiwi is a LuaJIT FFI binding for the Kiwi constraint solver. Kiwi is a fast kiwi is a LuaJIT FFI and Lua binding for the Kiwi constraint solver. Kiwi is a fast
implementation of the Cassowary constraint solving algorithm. ljkiwi provides implementation of the Cassowary constraint solving algorithm. kiwi provides
reasonably efficient bindings using the LuaJIT FFI.]], reasonably efficient bindings using the LuaJIT FFI and convential Lua C bindings.]],
license = "MIT", license = "MIT",
issues_url = "https://github.com/jkl1337/ljkiwi/issues", issues_url = "https://github.com/jkl1337/ljkiwi/issues",
maintainer = "John Luebs", maintainer = "John Luebs",
@@ -21,6 +21,7 @@ dependencies = {
build = { build = {
type = "make", type = "make",
build_variables = { build_variables = {
LUA = "$(LUA)",
CFLAGS = "$(CFLAGS)", CFLAGS = "$(CFLAGS)",
LUA_INCDIR = "$(LUA_INCDIR)", LUA_INCDIR = "$(LUA_INCDIR)",
LIBFLAG = "$(LIBFLAG)", LIBFLAG = "$(LIBFLAG)",

264
kiwi.lua
View File

@@ -4,20 +4,48 @@ local ffi
do do
local ffi_loader = package.preload["ffi"] local ffi_loader = package.preload["ffi"]
if ffi_loader == nil then if ffi_loader == nil then
return require("ckiwi") return require("ljkiwi")
end end
ffi = ffi_loader() --[[@as ffilib]] ffi = ffi_loader() --[[@as ffilib]]
end end
local kiwi = {} local kiwi = {}
local ckiwi local ljkiwi
do do
local cpath, err = package.searchpath("ckiwi", package.cpath) local cpath, err = package.searchpath("ljkiwi", package.cpath)
if cpath == nil then if cpath == nil then
error("kiwi dynamic library 'ckiwi' not found\n" .. err) error("kiwi dynamic library 'ljkiwi' not found\n" .. err)
end end
ckiwi = ffi.load(cpath) ljkiwi = ffi.load(cpath)
end
kiwi.ljkiwi = ljkiwi
ffi.cdef([[
void free(void *);
typedef struct KiwiTypeInfo {
unsigned size;
unsigned align;
} KiwiTypeInfo;
extern KiwiTypeInfo kiwi_ti_KiwiVar, kiwi_ti_KiwiConstraint, kiwi_ti_KiwiSolver;
]])
for _, t in ipairs({ "KiwiVar", "KiwiConstraint", "KiwiSolver" }) do
local tinfo = ljkiwi[("kiwi_ti_%s"):format(t)] --[[@as any]]
ffi.cdef(
[[
typedef struct $ {
unsigned char b_[$];
} __attribute__((aligned($))) $;
]],
t,
tinfo.size,
tinfo.align,
t
)
end end
ffi.cdef([[ ffi.cdef([[
@@ -37,17 +65,15 @@ enum KiwiErrKind {
enum KiwiRelOp { LE, GE, EQ }; enum KiwiRelOp { LE, GE, EQ };
typedef struct KiwiVarRefType* KiwiVarRef;
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
typedef struct KiwiTerm { typedef struct KiwiTerm {
KiwiVarRef var; KiwiVar* var;
double coefficient; double coefficient;
} KiwiTerm; } KiwiTerm;
typedef struct KiwiExpression { typedef struct KiwiExpression {
double constant; double constant;
int term_count; int term_count;
KiwiConstraint* owner;
KiwiTerm terms_[?]; KiwiTerm terms_[?];
} KiwiExpression; } KiwiExpression;
@@ -57,49 +83,52 @@ typedef struct KiwiErr {
bool must_free; bool must_free;
} KiwiErr; } KiwiErr;
typedef struct KiwiSolver { unsigned error_mask_; } KiwiSolver; struct KiwiSolver;
KiwiVarRef kiwi_var_new(const char* name); void kiwi_var_construct(const char* name, void* mem);
void kiwi_var_del(KiwiVarRef var); void kiwi_var_release(KiwiVar* var);
KiwiVarRef kiwi_var_clone(KiwiVarRef var); void kiwi_var_retain(KiwiVar* var);
const char* kiwi_var_name(KiwiVarRef var); const char* kiwi_var_name(const KiwiVar* var);
void kiwi_var_set_name(KiwiVarRef var, const char* name); void kiwi_var_set_name(KiwiVar* var, const char* name);
double kiwi_var_value(KiwiVarRef var); double kiwi_var_value(const KiwiVar* var);
void kiwi_var_set_value(KiwiVarRef var, double value); void kiwi_var_set_value(KiwiVar* var, double value);
bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other); bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other);
void kiwi_expression_del_vars(KiwiExpression* expr); void kiwi_expression_retain(KiwiExpression* expr);
void kiwi_expression_destroy(KiwiExpression* expr);
KiwiConstraintRef kiwi_constraint_new( void kiwi_constraint_construct(
const KiwiExpression* lhs, const KiwiExpression* lhs,
const KiwiExpression* rhs, const KiwiExpression* rhs,
enum KiwiRelOp op, enum KiwiRelOp op,
double strength double strength,
void* mem
); );
void kiwi_constraint_del(KiwiConstraintRef constraint); void kiwi_constraint_release(KiwiConstraint* c);
void kiwi_constraint_retain(KiwiConstraint* c);
double kiwi_constraint_strength(KiwiConstraintRef constraint); double kiwi_constraint_strength(const KiwiConstraint* c);
enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint); enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
bool kiwi_constraint_violated(KiwiConstraintRef constraint); bool kiwi_constraint_violated(const KiwiConstraint* c);
int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size); int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
KiwiSolver* kiwi_solver_new(unsigned error_mask); void kiwi_solver_construct(unsigned error_mask, void* mem);
void kiwi_solver_del(KiwiSolver* s); void kiwi_solver_destroy(KiwiSolver* s);
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* sp, KiwiConstraintRef constraint); const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
bool kiwi_solver_has_constraint(const KiwiSolver* sp, KiwiConstraintRef constraint); bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* sp, KiwiVarRef var, double strength); const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* sp, KiwiVarRef var); const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
bool kiwi_solver_has_edit_var(const KiwiSolver* sp, KiwiVarRef var); bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* sp, KiwiVarRef var, double value); const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
void kiwi_solver_update_vars(KiwiSolver* sp); void kiwi_solver_update_vars(KiwiSolver* sp);
void kiwi_solver_reset(KiwiSolver* sp); void kiwi_solver_reset(KiwiSolver* sp);
void kiwi_solver_dump(const KiwiSolver* sp); void kiwi_solver_dump(const KiwiSolver* sp);
char* kiwi_solver_dumps(const KiwiSolver* sp); char* kiwi_solver_dumps(const KiwiSolver* sp);
void free(void *);
]]) ]])
local strformat = string.format local strformat = string.format
@@ -159,7 +188,7 @@ do
end end
end end
local Var = ffi.typeof("struct KiwiVarRefType") --[[@as kiwi.Var]] local Var = ffi.typeof("struct KiwiVar") --[[@as kiwi.Var]]
kiwi.Var = Var kiwi.Var = Var
function kiwi.is_var(o) function kiwi.is_var(o)
@@ -173,14 +202,14 @@ function kiwi.is_term(o)
return ffi_istype(Term, o) return ffi_istype(Term, o)
end end
local Expression = ffi.typeof("struct KiwiExpression") --[[@as kiwi.Expression]] local Expression = ffi.typeof("KiwiExpression") --[[@as kiwi.Expression]]
kiwi.Expression = Expression kiwi.Expression = Expression
function kiwi.is_expression(o) function kiwi.is_expression(o)
return ffi_istype(Expression, o) return ffi_istype(Expression, o)
end end
local Constraint = ffi.typeof("struct KiwiConstraintRefType") --[[@as kiwi.Constraint]] local Constraint = ffi.typeof("KiwiConstraint") --[[@as kiwi.Constraint]]
kiwi.Constraint = Constraint kiwi.Constraint = Constraint
function kiwi.is_constraint(o) function kiwi.is_constraint(o)
@@ -192,18 +221,19 @@ end
---@param coeff number? ---@param coeff number?
---@nodiscard ---@nodiscard
local function add_expr_term(expr, var, coeff) local function add_expr_term(expr, var, coeff)
local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
for i = 0, expr.term_count - 1 do for i = 0, expr.term_count - 1 do
local st = expr.terms_[i] --[[@as kiwi.Term]] local st = expr.terms_[i] --[[@as kiwi.Term]]
local dt = ret.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(st.var) dt.var = st.var
dt.coefficient = st.coefficient dt.coefficient = st.coefficient
end end
local dt = ret.terms_[expr.term_count] local dt = ret.terms_[expr.term_count]
dt.var = ckiwi.kiwi_var_clone(var) dt.var = var
dt.coefficient = coeff or 1.0 dt.coefficient = coeff or 1.0
ret.constant = expr.constant ret.constant = expr.constant
ret.term_count = expr.term_count + 1 ret.term_count = expr.term_count + 1
ljkiwi.kiwi_expression_retain(ret)
return ret return ret
end end
@@ -212,12 +242,13 @@ end
---@param coeff number? ---@param coeff number?
---@nodiscard ---@nodiscard
local function new_expr_one(constant, var, coeff) local function new_expr_one(constant, var, coeff)
local ret = ffi_gc(ffi_new(Expression, 1), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, 1), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
local dt = ret.terms_[0] local dt = ret.terms_[0]
dt.var = ckiwi.kiwi_var_clone(var) dt.var = var
dt.coefficient = coeff or 1.0 dt.coefficient = coeff or 1.0
ret.constant = constant ret.constant = constant
ret.term_count = 1 ret.term_count = 1
ljkiwi.kiwi_var_retain(var)
return ret return ret
end end
@@ -228,15 +259,16 @@ end
---@param coeff2 number? ---@param coeff2 number?
---@nodiscard ---@nodiscard
local function new_expr_pair(constant, var1, var2, coeff1, coeff2) local function new_expr_pair(constant, var1, var2, coeff1, coeff2)
local ret = ffi_gc(ffi_new(Expression, 2), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, 2), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
local dt = ret.terms_[0] local dt = ret.terms_[0]
dt.var = ckiwi.kiwi_var_clone(var1) dt.var = var1
dt.coefficient = coeff1 or 1.0 dt.coefficient = coeff1 or 1.0
dt = ret.terms_[1] dt = ret.terms_[1]
dt.var = ckiwi.kiwi_var_clone(var2) dt.var = var2
dt.coefficient = coeff2 or 1.0 dt.coefficient = coeff2 or 1.0
ret.constant = constant ret.constant = constant
ret.term_count = 2 ret.term_count = 2
ljkiwi.kiwi_expression_retain(ret)
return ret return ret
end end
@@ -311,7 +343,9 @@ local function rel(lhs, rhs, op, strength)
op_error(lhs, rhs, OP_NAMES[op]) op_error(lhs, rhs, OP_NAMES[op])
end end
return ffi_gc(ckiwi.kiwi_constraint_new(el, er, op, strength or REQUIRED), ckiwi.kiwi_constraint_del) --[[@as kiwi.Constraint]] local c = ffi_new(Constraint)
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED, c)
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
end end
--- Define a constraint with expressions as `a <= b`. --- Define a constraint with expressions as `a <= b`.
@@ -357,22 +391,22 @@ do
--- Change the name of the variable. --- Change the name of the variable.
---@type fun(self: kiwi.Var, name: string) ---@type fun(self: kiwi.Var, name: string)
set_name = ckiwi.kiwi_var_set_name, set_name = ljkiwi.kiwi_var_set_name,
--- Get the current value of the variable. --- Get the current value of the variable.
---@type fun(self: kiwi.Var): number ---@type fun(self: kiwi.Var): number
value = ckiwi.kiwi_var_value, value = ljkiwi.kiwi_var_value,
--- Set the value of the variable. --- Set the value of the variable.
---@type fun(self: kiwi.Var, value: number) ---@type fun(self: kiwi.Var, value: number)
set = ckiwi.kiwi_var_set_value, set = ljkiwi.kiwi_var_set_value,
} }
--- Get the name of the variable. --- Get the name of the variable.
---@return string ---@return string
---@nodiscard ---@nodiscard
function Var_cls:name() function Var_cls:name()
return ffi_string(ckiwi.kiwi_var_name(self)) return ffi_string(ljkiwi.kiwi_var_name(self))
end end
--- Create a term from this variable. --- Create a term from this variable.
@@ -397,7 +431,9 @@ do
} }
function Var_mt:__new(name) function Var_mt:__new(name)
return ffi_gc(ckiwi.kiwi_var_new(name), ckiwi.kiwi_var_del) local v = ffi_new(self)
ljkiwi.kiwi_var_construct(name, v)
return ffi_gc(v, ljkiwi.kiwi_var_release)
end end
function Var_mt.__mul(a, b) function Var_mt.__mul(a, b)
@@ -441,6 +477,10 @@ do
return a + -b return a + -b
end end
function Var_mt:__eq(other)
return ljkiwi.kiwi_var_eq(self, other)
end
function Var_mt:__tostring() function Var_mt:__tostring()
return self:name() .. "(" .. self:value() .. ")" return self:name() .. "(" .. self:value() .. ")"
end end
@@ -482,11 +522,13 @@ do
local Term_mt = { __index = Term_cls } local Term_mt = { __index = Term_cls }
local function term_gc(term) local function term_gc(term)
ckiwi.kiwi_var_del(term.var) ljkiwi.kiwi_var_release(term.var)
end end
function Term_mt.__new(T, var, coefficient) function Term_mt.__new(T, var, coefficient)
return ffi_gc(ffi_new(T, ckiwi.kiwi_var_clone(var), coefficient or 1.0), term_gc) local t = ffi_gc(ffi_new(T, var, coefficient or 1.0), term_gc)
ljkiwi.kiwi_var_retain(var)
return t
end end
function Term_mt.__mul(a, b) function Term_mt.__mul(a, b)
@@ -542,6 +584,7 @@ do
---@class kiwi.Expression: ffi.cdata* ---@class kiwi.Expression: ffi.cdata*
---@overload fun(constant: number, ...: kiwi.Term): kiwi.Expression ---@overload fun(constant: number, ...: kiwi.Term): kiwi.Expression
---@field constant number ---@field constant number
---@field package owner ffi.cdata*
---@field package term_count number ---@field package term_count number
---@field package terms_ ffi.cdata* ---@field package terms_ ffi.cdata*
---@operator mul(number): kiwi.Expression ---@operator mul(number): kiwi.Expression
@@ -559,15 +602,16 @@ do
---@param constant number ---@param constant number
---@nodiscard ---@nodiscard
local function mul_expr_coeff(expr, constant) local function mul_expr_coeff(expr, constant)
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
for i = 0, expr.term_count - 1 do for i = 0, expr.term_count - 1 do
local st = expr.terms_[i] --[[@as kiwi.Term]] local st = expr.terms_[i] --[[@as kiwi.Term]]
local dt = ret.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(st.var) dt.var = st.var
dt.coefficient = st.coefficient * constant dt.coefficient = st.coefficient * constant
end end
ret.constant = expr.constant * constant ret.constant = expr.constant * constant
ret.term_count = expr.term_count ret.term_count = expr.term_count
ljkiwi.kiwi_expression_retain(ret)
return ret return ret
end end
@@ -577,22 +621,23 @@ do
local function add_expr_expr(a, b) local function add_expr_expr(a, b)
local a_count = a.term_count local a_count = a.term_count
local b_count = b.term_count local b_count = b.term_count
local ret = ffi_gc(ffi_new(Expression, a_count + b_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, a_count + b_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
for i = 0, a_count - 1 do for i = 0, a_count - 1 do
local dt = ret.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]]
local st = a.terms_[i] --[[@as kiwi.Term]] local st = a.terms_[i] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(st.var) dt.var = st.var
dt.coefficient = st.coefficient dt.coefficient = st.coefficient
end end
for i = 0, b_count - 1 do for i = 0, b_count - 1 do
local dt = ret.terms_[a_count + i] --[[@as kiwi.Term]] local dt = ret.terms_[a_count + i] --[[@as kiwi.Term]]
local st = b.terms_[i] --[[@as kiwi.Term]] local st = b.terms_[i] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(st.var) dt.var = st.var
dt.coefficient = st.coefficient dt.coefficient = st.coefficient
end end
ret.constant = a.constant + b.constant ret.constant = a.constant + b.constant
ret.term_count = a_count + b_count ret.term_count = a_count + b_count
ljkiwi.kiwi_expression_retain(ret)
return ret return ret
end end
@@ -600,16 +645,17 @@ 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_gc(ffi_new(Expression, expr.term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
for i = 0, expr.term_count - 1 do for i = 0, expr.term_count - 1 do
local dt = ret.terms_[i] --[[@as kiwi.Term]] local dt = ret.terms_[i] --[[@as kiwi.Term]]
local st = expr.terms_[i] --[[@as kiwi.Term]] local st = expr.terms_[i] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(st.var) dt.var = st.var
dt.coefficient = st.coefficient dt.coefficient = st.coefficient
end end
ret.constant = constant ret.constant = constant
ret.term_count = expr.term_count ret.term_count = expr.term_count
ljkiwi.kiwi_expression_retain(ret)
return ret return ret
end end
@@ -645,17 +691,18 @@ do
__index = Expression_cls, __index = Expression_cls,
} }
function Expression_mt.__new(T, constant, ...) function Expression_mt:__new(constant, ...)
local term_count = select("#", ...) local term_count = select("#", ...)
local e = ffi_gc(ffi_new(T, term_count), ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] local e = ffi_gc(ffi_new(self, term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
e.term_count = term_count e.term_count = term_count
e.constant = constant e.constant = constant
for i = 1, term_count do for i = 1, term_count do
local t = select(i, ...) local t = select(i, ...)
local dt = e.terms_[i - 1] --[[@as kiwi.Term]] local dt = e.terms_[i - 1] --[[@as kiwi.Term]]
dt.var = ckiwi.kiwi_var_clone(t.var) dt.var = t.var
dt.coefficient = t.coefficient dt.coefficient = t.coefficient
end end
ljkiwi.kiwi_expression_retain(e)
return e return e
end end
@@ -722,29 +769,29 @@ do
local Constraint_cls = { local Constraint_cls = {
--- The strength of the constraint. --- The strength of the constraint.
---@type fun(self: kiwi.Constraint): number ---@type fun(self: kiwi.Constraint): number
strength = ckiwi.kiwi_constraint_strength, strength = ljkiwi.kiwi_constraint_strength,
--- The relational operator of the constraint. --- The relational operator of the constraint.
---@type fun(self: kiwi.Constraint): kiwi.RelOp ---@type fun(self: kiwi.Constraint): kiwi.RelOp
op = ckiwi.kiwi_constraint_op, op = ljkiwi.kiwi_constraint_op,
--- Whether the constraint is violated in the current solution. --- Whether the constraint is violated in the current solution.
---@type fun(self: kiwi.Constraint): boolean ---@type fun(self: kiwi.Constraint): boolean
violated = ckiwi.kiwi_constraint_violated, violated = ljkiwi.kiwi_constraint_violated,
} }
--- The reduced expression defining the constraint. --- The reduced expression defining the constraint.
---@return kiwi.Expression ---@return kiwi.Expression
---@nodiscard ---@nodiscard
function Constraint_cls:expression() function Constraint_cls:expression()
local SZ = 7 -- 2**7 bytes on x64 local SZ = 7
local expr = ffi_new(Expression, SZ) --[[@as kiwi.Expression]] local expr = ffi_new(Expression, SZ) --[[@as kiwi.Expression]]
local n = ckiwi.kiwi_constraint_expression(self, expr, SZ) local n = ljkiwi.kiwi_constraint_expression(self, expr, SZ)
if n > SZ then if n > SZ then
expr = ffi_new(Expression, n) --[[@as kiwi.Expression]] expr = ffi_new(Expression, n) --[[@as kiwi.Expression]]
n = ckiwi.kiwi_constraint_expression(self, expr, n) n = ljkiwi.kiwi_constraint_expression(self, expr, n)
end end
return ffi_gc(expr, ckiwi.kiwi_expression_del_vars) --[[@as kiwi.Expression]] return ffi_gc(expr, ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
end end
--- Add the constraint to the solver. --- Add the constraint to the solver.
@@ -773,10 +820,9 @@ do
} }
function Constraint_mt:__new(lhs, rhs, op, strength) function Constraint_mt:__new(lhs, rhs, op, strength)
return ffi_gc( local c = ffi_new(self)
ckiwi.kiwi_constraint_new(lhs, rhs, op or "EQ", strength or REQUIRED), ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED, c)
ckiwi.kiwi_constraint_del return ffi_gc(c, ljkiwi.kiwi_constraint_release)
)
end end
local OPS = { [0] = "<=", ">=", "==" } local OPS = { [0] = "<=", ">=", "==" }
@@ -822,10 +868,9 @@ do
tmpexpr.constant = constant ~= nil and constant or 0 tmpexpr.constant = constant ~= nil and constant or 0
tmpexpr.term_count = 2 tmpexpr.term_count = 2
return ffi_gc( local c = ffi_new(Constraint)
ckiwi.kiwi_constraint_new(tmpexpr, nil, op or "EQ", strength or REQUIRED), ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
ckiwi.kiwi_constraint_del return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
) --[[@as kiwi.Constraint]]
end end
local pair_ratio = constraints.pair_ratio local pair_ratio = constraints.pair_ratio
@@ -858,10 +903,10 @@ do
local t = tmpexpr.terms_[0] local t = tmpexpr.terms_[0]
t.var = var t.var = var
t.coefficient = 1.0 t.coefficient = 1.0
return ffi_gc(
ckiwi.kiwi_constraint_new(tmpexpr, nil, op or "EQ", strength or REQUIRED), local c = ffi_new(Constraint)
ckiwi.kiwi_constraint_del ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
) --[[@as kiwi.Constraint]] return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
end end
end end
@@ -947,7 +992,7 @@ do
C.free(err) C.free(err)
end end
local errdata = new_error(kind, message, solver, item) local errdata = new_error(kind, message, solver, item)
local error_mask = solver and solver.error_mask_ or 0 local error_mask = ljkiwi.kiwi_solver_get_error_mask(solver)
return item, return item,
band(error_mask, lshift(1, kind --[[@as integer]])) == 0 and error(errdata) band(error_mask, lshift(1, kind --[[@as integer]])) == 0 and error(errdata)
or errdata or errdata
@@ -955,20 +1000,19 @@ do
return item return item
end end
---@class kiwi.Solver: ffi.cdata* ---@class kiwi.Solver: ffi.cdata*
---@field package error_mask_ integer
---@overload fun(error_mask: (integer|(kiwi.ErrKind|integer)[] )?): kiwi.Solver ---@overload fun(error_mask: (integer|(kiwi.ErrKind|integer)[] )?): kiwi.Solver
local Solver_cls = { local Solver_cls = {
--- Test whether a constraint is in the solver. --- Test whether a constraint is in the solver.
---@type fun(self: kiwi.Solver, constraint: kiwi.Constraint): boolean ---@type fun(self: kiwi.Solver, constraint: kiwi.Constraint): boolean
has_constraint = ckiwi.kiwi_solver_has_constraint, has_constraint = ljkiwi.kiwi_solver_has_constraint,
--- Test whether an edit variable has been added to the solver. --- Test whether an edit variable has been added to the solver.
---@type fun(self: kiwi.Solver, var: kiwi.Var): boolean ---@type fun(self: kiwi.Solver, var: kiwi.Var): boolean
has_edit_var = ckiwi.kiwi_solver_has_edit_var, has_edit_var = ljkiwi.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 = ljkiwi.kiwi_solver_update_vars,
--- Reset the solver to the empty starting conditions. --- Reset the solver to the empty starting conditions.
--- ---
@@ -978,11 +1022,11 @@ do
--- when the entire system must change, since it can avoid unecessary --- when the entire system must change, since it can avoid unecessary
--- heap (de)allocations. --- heap (de)allocations.
---@type fun(self: kiwi.Solver) ---@type fun(self: kiwi.Solver)
reset = ckiwi.kiwi_solver_reset, reset = ljkiwi.kiwi_solver_reset,
--- Dump a representation of the solver to stdout. --- Dump a representation of the solver to stdout.
---@type fun(self: kiwi.Solver) ---@type fun(self: kiwi.Solver)
dump = ckiwi.kiwi_solver_dump, dump = ljkiwi.kiwi_solver_dump,
} }
--- Sets the error mask for the solver. --- Sets the error mask for the solver.
@@ -992,7 +1036,7 @@ do
if type(mask) == "table" then if type(mask) == "table" then
mask = kiwi.error_mask(mask, invert) mask = kiwi.error_mask(mask, invert)
end end
self.error_mask_ = mask ljkiwi.kiwi_solver_set_error_mask(self, mask)
end end
---@generic T ---@generic T
@@ -1017,7 +1061,7 @@ do
---@param constraint kiwi.Constraint ---@param constraint kiwi.Constraint
---@return kiwi.Constraint constraint, kiwi.Error? ---@return kiwi.Constraint constraint, kiwi.Error?
function Solver_cls:add_constraint(constraint) function Solver_cls:add_constraint(constraint)
return try_solver(ckiwi.kiwi_solver_add_constraint, self, constraint) return try_solver(ljkiwi.kiwi_solver_add_constraint, self, constraint)
end end
--- Add constraints to the solver. --- Add constraints to the solver.
@@ -1027,7 +1071,7 @@ do
---@param constraints kiwi.Constraint[] ---@param constraints kiwi.Constraint[]
---@return kiwi.Constraint[] constraints, kiwi.Error? ---@return kiwi.Constraint[] constraints, kiwi.Error?
function Solver_cls:add_constraints(constraints) function Solver_cls:add_constraints(constraints)
return add_remove_items(self, constraints, ckiwi.kiwi_solver_add_constraint) return add_remove_items(self, constraints, ljkiwi.kiwi_solver_add_constraint)
end end
--- Remove a constraint from the solver. --- Remove a constraint from the solver.
@@ -1036,7 +1080,7 @@ do
---@param constraint kiwi.Constraint ---@param constraint kiwi.Constraint
---@return kiwi.Constraint constraint, kiwi.Error? ---@return kiwi.Constraint constraint, kiwi.Error?
function Solver_cls:remove_constraint(constraint) function Solver_cls:remove_constraint(constraint)
return try_solver(ckiwi.kiwi_solver_remove_constraint, self, constraint) return try_solver(ljkiwi.kiwi_solver_remove_constraint, self, constraint)
end end
--- Remove constraints from the solver. --- Remove constraints from the solver.
@@ -1045,7 +1089,7 @@ do
---@param constraints kiwi.Constraint[] ---@param constraints kiwi.Constraint[]
---@return kiwi.Constraint[] constraints, kiwi.Error? ---@return kiwi.Constraint[] constraints, kiwi.Error?
function Solver_cls:remove_constraints(constraints) function Solver_cls:remove_constraints(constraints)
return add_remove_items(self, constraints, ckiwi.kiwi_solver_remove_constraint) return add_remove_items(self, constraints, ljkiwi.kiwi_solver_remove_constraint)
end end
--- Add an edit variables to the solver. --- Add an edit variables to the solver.
@@ -1059,7 +1103,7 @@ do
---@param strength number the strength of the edit variable (must be less than `Strength.REQUIRED`) ---@param strength number the strength of the edit variable (must be less than `Strength.REQUIRED`)
---@return kiwi.Var var, kiwi.Error? ---@return kiwi.Var var, kiwi.Error?
function Solver_cls:add_edit_var(var, strength) function Solver_cls:add_edit_var(var, strength)
return try_solver(ckiwi.kiwi_solver_add_edit_var, self, var, strength) return try_solver(ljkiwi.kiwi_solver_add_edit_var, self, var, strength)
end end
--- Add edit variables to the solver. --- Add edit variables to the solver.
@@ -1073,7 +1117,7 @@ do
---@param strength number the strength of the edit variables (must be less than `Strength.REQUIRED`) ---@param strength number the strength of the edit variables (must be less than `Strength.REQUIRED`)
---@return kiwi.Var[] vars, kiwi.Error? ---@return kiwi.Var[] vars, kiwi.Error?
function Solver_cls:add_edit_vars(vars, strength) function Solver_cls:add_edit_vars(vars, strength)
return add_remove_items(self, vars, ckiwi.kiwi_solver_add_edit_var, strength) return add_remove_items(self, vars, ljkiwi.kiwi_solver_add_edit_var, strength)
end end
--- Remove an edit variable from the solver. --- Remove an edit variable from the solver.
@@ -1082,7 +1126,7 @@ do
---@param var kiwi.Var the edit variable to remove ---@param var kiwi.Var the edit variable to remove
---@return kiwi.Var var, kiwi.Error? ---@return kiwi.Var var, kiwi.Error?
function Solver_cls:remove_edit_var(var) function Solver_cls:remove_edit_var(var)
return try_solver(ckiwi.kiwi_solver_remove_edit_var, self, var) return try_solver(ljkiwi.kiwi_solver_remove_edit_var, self, var)
end end
--- Removes edit variables from the solver. --- Removes edit variables from the solver.
@@ -1091,7 +1135,7 @@ do
---@param vars kiwi.Var[] the edit variables to remove ---@param vars kiwi.Var[] the edit variables to remove
---@return kiwi.Var[] vars, kiwi.Error? ---@return kiwi.Var[] vars, kiwi.Error?
function Solver_cls:remove_edit_vars(vars) function Solver_cls:remove_edit_vars(vars)
return add_remove_items(self, vars, ckiwi.kiwi_solver_remove_edit_var) return add_remove_items(self, vars, ljkiwi.kiwi_solver_remove_edit_var)
end end
--- Suggest a value for the given edit variable. --- Suggest a value for the given edit variable.
@@ -1104,7 +1148,7 @@ do
---@param value number the suggested value ---@param value number the suggested value
---@return kiwi.Var var, kiwi.Error? ---@return kiwi.Var var, kiwi.Error?
function Solver_cls:suggest_value(var, value) function Solver_cls:suggest_value(var, value)
return try_solver(ckiwi.kiwi_solver_suggest_value, self, var, value) return try_solver(ljkiwi.kiwi_solver_suggest_value, self, var, value)
end end
--- Suggest values for the given edit variables. --- Suggest values for the given edit variables.
@@ -1116,7 +1160,7 @@ do
---@return kiwi.Var[] vars, number[] values, kiwi.Error? ---@return kiwi.Var[] vars, number[] values, kiwi.Error?
function Solver_cls:suggest_values(vars, values) function Solver_cls:suggest_values(vars, values)
for i, var in ipairs(vars) do for i, var in ipairs(vars) do
local _, err = try_solver(ckiwi.kiwi_solver_suggest_value, self, var, values[i]) local _, err = try_solver(ljkiwi.kiwi_solver_suggest_value, self, var, values[i])
if err ~= nil then if err ~= nil then
return vars, values, err return vars, values, err
end end
@@ -1128,7 +1172,7 @@ do
---@return string ---@return string
---@nodiscard ---@nodiscard
function Solver_cls:dumps() function Solver_cls:dumps()
local cs = ckiwi.kiwi_solver_dumps(self) local cs = ljkiwi.kiwi_solver_dumps(self)
local s = ffi_string(cs) local s = ffi_string(cs)
C.free(cs) C.free(cs)
return s return s
@@ -1138,17 +1182,23 @@ do
__index = Solver_cls, __index = Solver_cls,
} }
local Solver = ffi.typeof("struct KiwiSolver") --[[@as kiwi.Solver]]
kiwi.Solver = Solver
function Solver_mt:__new(error_mask) function Solver_mt:__new(error_mask)
if type(error_mask) == "table" then if type(error_mask) == "table" then
error_mask = kiwi.error_mask(error_mask) error_mask = kiwi.error_mask(error_mask)
end end
return ffi_gc(ckiwi.kiwi_solver_new(error_mask or 0), ckiwi.kiwi_solver_del)
local s = ffi_new(Solver)
ljkiwi.kiwi_solver_construct(error_mask or 0, s)
return ffi_gc(s, ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
end end
kiwi.Solver = ffi.metatype("struct KiwiSolver", Solver_mt) --[[@as kiwi.Solver]] kiwi.Solver = ffi.metatype(Solver, Solver_mt) --[[@as kiwi.Solver]]
function kiwi.is_solver(s) function kiwi.is_solver(s)
return ffi_istype(kiwi.Solver, s) return ffi_istype(Solver, s)
end end
end end

37
ljkiwi.def Normal file
View File

@@ -0,0 +1,37 @@
EXPORTS
luaopen_ljkiwi
kiwi_ti_KiwiVar
kiwi_ti_KiwiConstraint
kiwi_ti_KiwiSolver
kiwi_constraint_construct
kiwi_constraint_expression
kiwi_constraint_op
kiwi_constraint_release
kiwi_constraint_retain
kiwi_constraint_strength
kiwi_constraint_violated
kiwi_expression_destroy
kiwi_solver_add_constraint
kiwi_solver_add_edit_var
kiwi_solver_construct
kiwi_solver_destroy
kiwi_solver_dump
kiwi_solver_dumps
kiwi_solver_has_constraint
kiwi_solver_has_edit_var
kiwi_solver_remove_constraint
kiwi_solver_remove_edit_var
kiwi_solver_reset
kiwi_solver_suggest_value
kiwi_solver_update_vars
kiwi_var_construct
kiwi_var_destroy
kiwi_var_eq
kiwi_var_name
kiwi_var_release
kiwi_var_retain
kiwi_var_set_name
kiwi_var_set_value
kiwi_var_value

1
ljkiwi.hpp Normal file
View File

@@ -0,0 +1 @@
#include <kiwi/kiwi.h>

View File

@@ -1,88 +0,0 @@
AccessModifierOffset: -2
AlignAfterOpenBracket: BlockIndent # New in v14. For earlier clang-format versions, use AlwaysBreak instead.
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: false
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakAfterAttributes: Always
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: false
ColumnLimit: 98
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
IndentWidth: 3
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PointerAlignment: Left
ReferenceAlignment: Left # New in v13. int &name ==> int& name
ReflowComments: false
SeparateDefinitionBlocks: Always # New in v14.
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never

View File

@@ -4,21 +4,24 @@
#include <kiwi/kiwi.h> #include <kiwi/kiwi.h>
#include <cstring> #include <cstring>
#include <new>
#include "luacompat.h" #include "luacompat.h"
#define ARR_COUNT(x) ((int)(sizeof(x) / sizeof((x)[0]))) #if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
#if defined(__GNUC__) && !defined(LKIWI_NO_BUILTIN) #define lk_unlikely(x) (__builtin_expect(((x) != 0), 0))
#define l_likely(x) (__builtin_expect(((x) != 0), 1))
#define l_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else #else
#define l_likely(x) (x) #define lk_likely(x) (x)
#define l_unlikely(x) (x) #define lk_unlikely(x) (x)
#endif #endif
namespace {
using namespace kiwi;
// Lua 5.1 compatibility for missing lua_arith. // Lua 5.1 compatibility for missing lua_arith.
static void compat_arith_unm(lua_State* L) { inline void compat_arith_unm(lua_State* L) {
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
lua_Number n = lua_tonumber(L, -1); lua_Number n = lua_tonumber(L, -1);
if (n != 0 || lua_isnumber(L, -1)) { if (n != 0 || lua_isnumber(L, -1)) {
@@ -35,14 +38,13 @@ static void compat_arith_unm(lua_State* L) {
} }
// This version supports placeholders. // This version supports placeholders.
static void setfuncs(lua_State* L, const luaL_Reg* l, int nup) { inline void setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
luaL_checkstack(L, nup, "too many upvalues"); luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */ for (; l->name != NULL; l++) { /* fill the table with given functions */
if (l->func == NULL) /* place holder? */ if (l->func == NULL) /* place holder? */
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
else { else {
int i; for (int i = 0; i < nup; i++) /* copy upvalues to the top */
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup); lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
} }
@@ -51,11 +53,15 @@ static void setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
lua_pop(L, nup); /* remove upvalues */ lua_pop(L, nup); /* remove upvalues */
} }
#define newlib(L, l) (lua_newtable((L)), setfuncs((L), (l), 0)) template<typename T, std::size_t N>
constexpr int array_count(T (&)[N]) {
return static_cast<int>(N);
}
namespace { inline void newlib(lua_State* L, const luaL_Reg* l) {
lua_newtable(L);
using namespace kiwi; setfuncs(L, l, 0);
}
enum KiwiErrKind { enum KiwiErrKind {
KiwiErrNone, KiwiErrNone,
@@ -80,15 +86,32 @@ struct KiwiExpression {
double constant; double constant;
int term_count; int term_count;
Constraint* owner; Constraint* owner;
#if !defined(_MSC_VER) || _MSC_VER >= 1900
KiwiTerm terms[];
static constexpr std::size_t sz(int count) {
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 0 ? count : 0);
}
#else
KiwiTerm terms[1]; KiwiTerm terms[1];
static constexpr std::size_t sz(int count) {
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 1 ? count - 1 : 0);
}
#endif
KiwiExpression() = delete;
KiwiExpression(const KiwiExpression&) = delete;
KiwiExpression& operator=(const KiwiExpression&) = delete;
~KiwiExpression() = delete;
}; };
// This structure was initially designed for LuaJIT FFI. It works OK for C++ // This mechanism was initially designed for LuaJIT FFI.
// though it certainly isn't idiomatic.
struct KiwiErr { struct KiwiErr {
enum KiwiErrKind kind; enum KiwiErrKind kind;
const char* message; const char* message;
bool must_free; bool must_delete;
}; };
struct KiwiSolver { struct KiwiSolver {
@@ -96,60 +119,59 @@ struct KiwiSolver {
Solver solver; Solver solver;
}; };
const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) { inline const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
if (!std::strcmp(ex.what(), base->message)) if (!std::strcmp(ex.what(), base->message))
return base; return base;
const auto msg_n = std::strlen(ex.what()) + 1; const auto msg_n = std::strlen(ex.what()) + 1;
auto* mem = static_cast<char*>(std::malloc(sizeof(KiwiErr) + msg_n)); auto* mem = static_cast<char*>(::operator new(sizeof(KiwiErr) + msg_n, std::nothrow));
if (!mem) { if (!mem) {
return base; return base;
} }
auto* msg = mem + sizeof(KiwiErr);
const auto* err = new (mem) KiwiErr {base->kind, mem + sizeof(KiwiErr), true}; std::memcpy(msg, ex.what(), msg_n);
std::memcpy(const_cast<char*>(err->message), ex.what(), msg_n); return new (mem) KiwiErr {base->kind, msg, true};
return err;
} }
const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown,
"An unhandled C++ exception occurred."};
template<typename F> template<typename F>
inline const KiwiErr* wrap_err(F&& f) { inline const KiwiErr* wrap_err(F&& f) {
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown,
"An unhandled C++ exception occurred."};
try { try {
f(); f();
} catch (const UnsatisfiableConstraint& ex) { } catch (const UnsatisfiableConstraint&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnsatisfiableConstraint, KiwiErrUnsatisfiableConstraint,
"The constraint cannot be satisfied."}; "The constraint cannot be satisfied."};
return &err; return &err;
} catch (const UnknownConstraint& ex) { } catch (const UnknownConstraint&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnknownConstraint, KiwiErrUnknownConstraint,
"The constraint has not been added to the solver."}; "The constraint has not been added to the solver."};
return &err; return &err;
} catch (const DuplicateConstraint& ex) { } catch (const DuplicateConstraint&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrDuplicateConstraint, KiwiErrDuplicateConstraint,
"The constraint has already been added to the solver."}; "The constraint has already been added to the solver."};
return &err; return &err;
} catch (const UnknownEditVariable& ex) { } catch (const UnknownEditVariable&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrUnknownEditVariable, KiwiErrUnknownEditVariable,
"The edit variable has not been added to the solver."}; "The edit variable has not been added to the solver."};
return &err; return &err;
} catch (const DuplicateEditVariable& ex) { } catch (const DuplicateEditVariable&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrDuplicateEditVariable, KiwiErrDuplicateEditVariable,
"The edit variable has already been added to the solver."}; "The edit variable has already been added to the solver."};
return &err; return &err;
} catch (const BadRequiredStrength& ex) { } catch (const BadRequiredStrength&) {
static const constexpr KiwiErr err { static const constexpr KiwiErr err {
KiwiErrBadRequiredStrength, KiwiErrBadRequiredStrength,
"A required strength cannot be used in this context."}; "A required strength cannot be used in this context."};
@@ -193,12 +215,12 @@ inline Constraint* kiwi_constraint_retain(Constraint* c) {
return c; return c;
} }
inline void kiwi_constraint_new( inline Constraint* kiwi_constraint_new(
const KiwiExpression* lhs, const KiwiExpression* lhs,
const KiwiExpression* rhs, const KiwiExpression* rhs,
RelationalOperator op, RelationalOperator op,
double strength, double strength,
Constraint* mem void* mem
) { ) {
if (strength < 0.0) { if (strength < 0.0) {
strength = kiwi::strength::required; strength = kiwi::strength::required;
@@ -217,7 +239,7 @@ inline void kiwi_constraint_new(
terms.emplace_back(*t->var, -t->coefficient); terms.emplace_back(*t->var, -t->coefficient);
} }
} }
new (mem) Constraint( return new (mem) Constraint(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)), Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op), static_cast<RelationalOperator>(op),
strength strength

File diff suppressed because it is too large Load Diff

12
t.lua
View File

@@ -8,14 +8,16 @@ do
local v3 = kiwi.Var("v3") local v3 = kiwi.Var("v3")
local v4 = kiwi.Var("v4") local v4 = kiwi.Var("v4")
local v5 = kiwi.Var("v5") local v5 = kiwi.Var("v5")
local c = (3 * v1 + 4 * v2 + 6 * v3):eq(0) local c1 = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
local c2 = (6 * v4 + 4 * v5 + 2 * v1 + 1.4 * v1 / 0.3):le(1000)
local e = c:expression() local e = c1:expression()
print(e) local s = kiwi.Solver()
s:add_constraints({ c1, c2, c1 })
print(s:dumps())
end end
collectgarbage("collect")
collectgarbage("collect")
-- c = (3 * v1 + 4 * v2 + 6 * v3):eq(0) -- c = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
-- local t = c:expression():terms() -- local t = c:expression():terms()