#include "ljkiwi.hpp" #include "ckiwi.h" #include #include #include #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 { using namespace kiwi; const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) { if (!std::strcmp(ex.what(), base->message)) return base; const auto msg_n = std::strlen(ex.what()) + 1; auto* mem = static_cast(std::malloc(sizeof(KiwiErr) + msg_n)); if (!mem) { return base; } const auto* err = new (mem) KiwiErr {base->kind, mem + sizeof(KiwiErr), true}; std::memcpy(const_cast(err->message), ex.what(), msg_n); return err; } static const constexpr KiwiErr kKiwiErrUnhandledCxxException { KiwiErrUnknown, "An unhandled C++ exception occurred."}; static const constexpr KiwiErr kKiwiErrNullObjectArg0 { KiwiErrNullObject, "null object passed as argument #0 (self)"}; static const constexpr KiwiErr kKiwiErrNullObjectArg1 { KiwiErrNullObject, "null object passed as argument #1"}; template inline const KiwiErr* wrap_err(F&& f) { try { f(); } catch (const UnsatisfiableConstraint& ex) { static const constexpr KiwiErr err { KiwiErrUnsatisfiableConstraint, "The constraint cannot be satisfied."}; return &err; } catch (const UnknownConstraint& ex) { static const constexpr KiwiErr err { KiwiErrUnknownConstraint, "The constraint has not been added to the solver."}; return &err; } catch (const DuplicateConstraint& ex) { static const constexpr KiwiErr err { KiwiErrDuplicateConstraint, "The constraint has already been added to the solver."}; return &err; } catch (const UnknownEditVariable& ex) { static const constexpr KiwiErr err { KiwiErrUnknownEditVariable, "The edit variable has not been added to the solver."}; return &err; } catch (const DuplicateEditVariable& ex) { static const constexpr KiwiErr err { KiwiErrDuplicateEditVariable, "The edit variable has already been added to the solver."}; return &err; } catch (const BadRequiredStrength& ex) { static const constexpr KiwiErr err { KiwiErrBadRequiredStrength, "A required strength cannot be used in this context."}; return &err; } catch (const InternalSolverError& ex) { static const constexpr KiwiErr base { KiwiErrInternalSolverError, "An internal solver error occurred."}; return new_error(&base, ex); } catch (std::bad_alloc&) { static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."}; return &err; } catch (const std::exception& ex) { return new_error(&kKiwiErrUnhandledCxxException, ex); } catch (...) { return &kKiwiErrUnhandledCxxException; } return nullptr; } template inline const KiwiErr* wrap_err(P self, F&& f) { if (lk_unlikely(!self)) { return &kKiwiErrNullObjectArg0; } return wrap_err([&]() { f(self->solver); }); } template inline const KiwiErr* wrap_err(P self, R item, F&& f) { if (lk_unlikely(!self)) { return &kKiwiErrNullObjectArg0; } else if (lk_unlikely(!item)) { return &kKiwiErrNullObjectArg1; } return wrap_err([&]() { f(self->solver, *item); }); } } // namespace extern "C" { KiwiTypeInfo kiwi_ti_KiwiVar = {sizeof(KiwiVar), alignof(KiwiVar)}; 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_release(KiwiVar* var) { if (lk_likely(var)) var->~KiwiVar(); } void kiwi_var_retain(KiwiVar* var) { if (lk_likely(var)) { alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)]; new (buf) KiwiVar(*var); } } const char* kiwi_var_name(const KiwiVar* var) { return lk_likely(var) ? var->name().c_str() : "()"; } 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::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* rhs, enum KiwiRelOp op, double strength, void* mem ) { if (strength < 0.0) { strength = kiwi::strength::required; } std::vector terms; terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0)); if (lhs) { for (auto* t = lhs->terms_; t != lhs->terms_ + lhs->term_count; ++t) { if (t->var) terms.emplace_back(*t->var, t->coefficient); } } if (rhs) { for (auto* t = rhs->terms_; t != rhs->terms_ + rhs->term_count; ++t) { if (t->var) terms.emplace_back(*t->var, -t->coefficient); } } new (mem) Constraint( Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)), static_cast(op), strength ); } void kiwi_constraint_release(KiwiConstraint* c) { if (lk_likely(c)) c->~KiwiConstraint(); } void kiwi_constraint_retain(KiwiConstraint* c) { if (lk_likely(c)) { alignas(KiwiConstraint) unsigned char buf[sizeof(KiwiConstraint)]; new (buf) KiwiConstraint(*c); } } double kiwi_constraint_strength(const KiwiConstraint* c) { return lk_likely(c) ? c->strength() : std::numeric_limits::quiet_NaN(); } enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c) { return lk_likely(c) ? static_cast(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(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(&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 { unsigned error_mask; Solver solver; }; KiwiTypeInfo kiwi_ti_KiwiSolver = {sizeof(KiwiSolver), alignof(KiwiSolver)}; void kiwi_solver_construct(unsigned error_mask, void* mem) { new (mem) KiwiSolver {error_mask}; } void kiwi_solver_destroy(KiwiSolver* s) { if (lk_likely(s)) s->~KiwiSolver(); } unsigned kiwi_solver_get_error_mask(const KiwiSolver* s) { return lk_likely(s) ? s->error_mask : 0; } void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask) { if (lk_likely(s)) s->error_mask = mask; } const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint) { return wrap_err(s, constraint, [](auto& s, const auto& c) { s.addConstraint(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 s->solver.hasConstraint(*constraint); } const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength) { return wrap_err(s, var, [strength](auto& s, const auto& v) { s.addEditVariable(v, strength); }); } const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var) { return wrap_err(s, var, [](auto& s, const auto& v) { s.removeEditVariable(v); }); } bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var) { if (lk_unlikely(!s || !var)) return 0; return s->solver.hasEditVariable(*var); } const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value) { return wrap_err(s, var, [value](auto& s, const auto& v) { s.suggestValue(v, value); }); } void kiwi_solver_update_vars(KiwiSolver* s) { if (lk_likely(s)) s->solver.updateVariables(); } void kiwi_solver_reset(KiwiSolver* s) { if (lk_likely(s)) s->solver.reset(); } void kiwi_solver_dump(const KiwiSolver* s) { if (lk_likely(s)) s->solver.dump(); } char* kiwi_solver_dumps(const KiwiSolver* s) { if (lk_unlikely(!s)) return nullptr; const auto& str = s->solver.dumps(); const auto buf_size = str.size() + 1; auto* buf = static_cast(std::malloc(buf_size)); if (!buf) return nullptr; std::memcpy(buf, str.c_str(), str.size() + 1); return buf; } } // extern "C"