#ifndef LUAKIWI_INT_H_ #define LUAKIWI_INT_H_ #include #include #include #include "luacompat.h" #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; // Lua 5.1 compatibility for missing lua_arith. inline 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. inline 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 { for (int 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 */ } template constexpr int array_count(T (&)[N]) { return static_cast(N); } inline void newlib(lua_State* L, const luaL_Reg* l) { lua_newtable(L); setfuncs(L, l, 0); } 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; #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]; 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 mechanism was initially designed for LuaJIT FFI. struct KiwiErr { enum KiwiErrKind kind; const char* message; bool must_delete; }; struct KiwiSolver { unsigned error_mask; Solver solver; }; inline 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(::operator new(sizeof(KiwiErr) + msg_n, std::nothrow)); if (!mem) { return base; } auto* msg = mem + sizeof(KiwiErr); std::memcpy(msg, ex.what(), msg_n); return new (mem) KiwiErr {base->kind, msg, true}; } template inline const KiwiErr* wrap_err(F&& f) { static const constexpr KiwiErr kKiwiErrUnhandledCxxException { KiwiErrUnknown, "An unhandled C++ exception occurred."}; 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 Constraint* kiwi_constraint_new( const KiwiExpression* lhs, const KiwiExpression* rhs, RelationalOperator 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) { 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); } } return 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_