#ifndef LUAKIWI_INT_H_ #define LUAKIWI_INT_H_ #include #include #include "luacompat.h" #define ARR_COUNT(x) ((int)(sizeof(x) / sizeof((x)[0]))) #if defined(__GNUC__) && !defined(LKIWI_NO_BUILTIN) #define l_likely(x) (__builtin_expect(((x) != 0), 1)) #define l_unlikely(x) (__builtin_expect(((x) != 0), 0)) #else #define l_likely(x) (x) #define l_unlikely(x) (x) #endif // Lua 5.1 compatibility for missing lua_arith. static void compat_arith_unm(lua_State* L) { #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 lua_Number n = lua_tonumber(L, -1); if (n != 0 || lua_isnumber(L, -1)) { lua_pop(L, 1); lua_pushnumber(L, -n); } else { if (!luaL_callmeta(L, -1, "__unm")) luaL_error(L, "attempt to perform arithmetic on a %s value", luaL_typename(L, -1)); lua_replace(L, -2); } #else lua_arith(L, LUA_OPUNM); #endif } // This version supports placeholders. static void setfuncs(lua_State* L, const luaL_Reg* l, int nup) { luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ if (l->func == NULL) /* place holder? */ lua_pushboolean(L, 0); else { int i; for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -nup); lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ } lua_setfield(L, -(nup + 2), l->name); } lua_pop(L, nup); /* remove upvalues */ } #define newlib(L, l) (lua_newtable((L)), setfuncs((L), (l), 0)) namespace { using namespace kiwi; enum KiwiErrKind { KiwiErrNone, KiwiErrUnsatisfiableConstraint = 1, KiwiErrUnknownConstraint, KiwiErrDuplicateConstraint, KiwiErrUnknownEditVariable, KiwiErrDuplicateEditVariable, KiwiErrBadRequiredStrength, KiwiErrInternalSolverError, KiwiErrAlloc, KiwiErrNullObject, KiwiErrUnknown, }; struct KiwiTerm { Variable* var; double coefficient; }; struct KiwiExpression { double constant; int term_count; Constraint* owner; // https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html // Although using one-element arrays this way is discouraged, GCC handles accesses to // trailing one-element array members analogously to zero-length arrays. // The only reason avoiding FAM here is to support older MSVC. // Otherwise this is a non-issue. KiwiTerm terms[1]; }; // This structure was initially designed for LuaJIT FFI. It works OK for C++ // though it certainly isn't idiomatic. struct KiwiErr { enum KiwiErrKind kind; const char* message; bool must_free; }; struct KiwiSolver { unsigned error_mask; Solver solver; }; 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; } const constexpr KiwiErr kKiwiErrUnhandledCxxException { KiwiErrUnknown, "An unhandled C++ exception occurred."}; template inline const KiwiErr* wrap_err(F&& f) { try { f(); } catch (const UnsatisfiableConstraint&) { static const constexpr KiwiErr err { KiwiErrUnsatisfiableConstraint, "The constraint cannot be satisfied."}; return &err; } catch (const UnknownConstraint&) { static const constexpr KiwiErr err { KiwiErrUnknownConstraint, "The constraint has not been added to the solver."}; return &err; } catch (const DuplicateConstraint&) { static const constexpr KiwiErr err { KiwiErrDuplicateConstraint, "The constraint has already been added to the solver."}; return &err; } catch (const UnknownEditVariable&) { static const constexpr KiwiErr err { KiwiErrUnknownEditVariable, "The edit variable has not been added to the solver."}; return &err; } catch (const DuplicateEditVariable&) { static const constexpr KiwiErr err { KiwiErrDuplicateEditVariable, "The edit variable has already been added to the solver."}; return &err; } catch (const BadRequiredStrength&) { 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& s, F&& f) { return wrap_err([&]() { f(s); }); } template inline const KiwiErr* wrap_err(P& s, R& ref, F&& f) { return wrap_err([&]() { f(s, ref); }); } inline Variable* kiwi_var_retain(Variable* var) { alignas(Variable) unsigned char buf[sizeof(Variable)]; new (buf) Variable(*var); return var; } inline Constraint* kiwi_constraint_retain(Constraint* c) { alignas(Constraint) unsigned char buf[sizeof(Constraint)]; new (buf) Constraint(*c); return c; } inline void kiwi_constraint_new( const KiwiExpression* lhs, const KiwiExpression* rhs, RelationalOperator op, double strength, Constraint* 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) { terms.emplace_back(*t->var, t->coefficient); } } if (rhs) { for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) { 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 ); } inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, const Constraint& constraint) { return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.addConstraint(c); }); } inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, const Constraint& constraint) { return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.removeConstraint(c); }); } inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, const Variable& var, double strength) { return wrap_err(s, var, [strength](auto& solver, const auto& v) { solver.addEditVariable(v, strength); }); } inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, const Variable& var) { return wrap_err(s, var, [](auto& solver, const auto& v) { solver.removeEditVariable(v); }); } inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, const Variable& var, double value) { return wrap_err(s, var, [value](auto& solver, const auto& v) { solver.suggestValue(v, value); }); } } // namespace // Local Variables: // mode: c++ // End: #endif // LUAKIWI_INT_H_