Initial version, pending windows
This commit is contained in:
155
luakiwi/luacompat.h
Normal file
155
luakiwi/luacompat.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifndef LJKIWI_LUACOMPAT_H_
|
||||
#define LJKIWI_LUACOMPAT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 && defined(__GNUC__)
|
||||
#define LJKIWI_LJ_COMPAT_ATTR __attribute__((weak, visibility("default")))
|
||||
#else
|
||||
#define LJKIWI_LJ_COMPAT_ATTR static
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
|
||||
|
||||
#define LUA_OPADD 0
|
||||
#define LUA_OPSUB 1
|
||||
#define LUA_OPMUL 2
|
||||
#define LUA_OPDIV 3
|
||||
#define LUA_OPMOD 4
|
||||
#define LUA_OPPOW 5
|
||||
#define LUA_OPUNM 6
|
||||
|
||||
static int lua_absindex(lua_State* L, int i) {
|
||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||
i += lua_gettop(L) + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
LJKIWI_LJ_COMPAT_ATTR lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
|
||||
lua_Number n = lua_tonumber(L, i);
|
||||
if (isnum != NULL) {
|
||||
*isnum = (n != 0 || lua_isnumber(L, i));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
LJKIWI_LJ_COMPAT_ATTR lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
|
||||
int ok = 0;
|
||||
lua_Number n = lua_tonumberx(L, i, &ok);
|
||||
if (ok) {
|
||||
if (n == (lua_Integer)n) {
|
||||
if (isnum)
|
||||
*isnum = 1;
|
||||
return (lua_Integer)n;
|
||||
}
|
||||
}
|
||||
if (isnum)
|
||||
*isnum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* luaL_tolstring(lua_State* L, int idx, size_t* len) {
|
||||
if (!luaL_callmeta(L, idx, "__tostring")) {
|
||||
int t = lua_type(L, idx), tt = 0;
|
||||
char const* name = NULL;
|
||||
switch (t) {
|
||||
case LUA_TNIL:
|
||||
lua_pushliteral(L, "nil");
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
case LUA_TNUMBER:
|
||||
lua_pushvalue(L, idx);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, idx))
|
||||
lua_pushliteral(L, "true");
|
||||
else
|
||||
lua_pushliteral(L, "false");
|
||||
break;
|
||||
default:
|
||||
tt = luaL_getmetafield(L, idx, "__name");
|
||||
name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
|
||||
lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
|
||||
if (tt != LUA_TNIL)
|
||||
lua_replace(L, -2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
}
|
||||
return lua_tolstring(L, -1, len);
|
||||
}
|
||||
|
||||
#endif /* LUA_VERSION_NUM == 501 */
|
||||
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
|
||||
|
||||
static void compat_reverse(lua_State* L, int a, int b) {
|
||||
for (; a < b; ++a, --b) {
|
||||
lua_pushvalue(L, a);
|
||||
lua_pushvalue(L, b);
|
||||
lua_replace(L, a);
|
||||
lua_replace(L, b);
|
||||
}
|
||||
}
|
||||
|
||||
static void lua_rotate(lua_State* L, int idx, int n) {
|
||||
int n_elems = 0;
|
||||
idx = lua_absindex(L, idx);
|
||||
n_elems = lua_gettop(L) - idx + 1;
|
||||
if (n < 0)
|
||||
n += n_elems;
|
||||
if (n > 0 && n < n_elems) {
|
||||
luaL_checkstack(L, 2, "not enough stack slots available");
|
||||
n = n_elems - n;
|
||||
compat_reverse(L, idx, idx + n - 1);
|
||||
compat_reverse(L, idx + n, idx + n_elems - 1);
|
||||
compat_reverse(L, idx, idx + n_elems - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int lua_geti(lua_State* L, int index, lua_Integer i) {
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushinteger(L, i);
|
||||
lua_gettable(L, index);
|
||||
return lua_type(L, -1);
|
||||
}
|
||||
|
||||
#endif /* LUA_VERSION_NUM <= 502 */
|
||||
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503
|
||||
static int luaL_typeerror(lua_State* L, int arg, const char* tname) {
|
||||
const char* msg;
|
||||
const char* typearg; /* name for the type of the actual argument */
|
||||
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
||||
typearg = lua_tostring(L, -1); /* use the given type name */
|
||||
else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
|
||||
typearg = "light userdata"; /* special name for messages */
|
||||
else
|
||||
typearg = luaL_typename(L, arg); /* standard name */
|
||||
msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
|
||||
return luaL_argerror(L, arg, msg);
|
||||
}
|
||||
|
||||
#endif /* LUA_VERSION_NUM <= 503 */
|
||||
|
||||
#if !defined(luaL_newlibtable)
|
||||
#define luaL_newlibtable(L, l) lua_createtable(L, 0, sizeof(l) / sizeof((l)[0]) - 1)
|
||||
#endif
|
||||
|
||||
#if !defined(luaL_checkversion)
|
||||
#define luaL_checkversion(L) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LJKIWI_LUACOMPAT_H_
|
||||
301
luakiwi/luakiwi-int.h
Normal file
301
luakiwi/luakiwi-int.h
Normal file
@@ -0,0 +1,301 @@
|
||||
#ifndef LUAKIWI_INT_H_
|
||||
#define LUAKIWI_INT_H_
|
||||
|
||||
#include <kiwi/kiwi.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#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
|
||||
int isnum;
|
||||
lua_Number n = lua_tonumberx(L, -1, &isnum);
|
||||
if (isnum) {
|
||||
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<typename T, std::size_t N>
|
||||
constexpr int array_count(T (&)[N]) {
|
||||
return static_cast<int>(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 {
|
||||
VariableData* var;
|
||||
double coefficient;
|
||||
};
|
||||
|
||||
struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
ConstraintData* 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<char*>(::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<typename F>
|
||||
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<typename P, typename R, typename F>
|
||||
inline const KiwiErr* wrap_err(P&& s, F&& f) {
|
||||
return wrap_err([&]() { f(s); });
|
||||
}
|
||||
|
||||
template<typename P, typename R, typename F>
|
||||
inline const KiwiErr* wrap_err(P&& s, R&& ref, F&& f) {
|
||||
return wrap_err([&]() { f(s, ref); });
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline T* make_unmanaged(Args... args) {
|
||||
auto* o = new T(std::forward<Args>(args)...);
|
||||
o->m_refcount = 1;
|
||||
return o;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void release_unmanaged(T* p) {
|
||||
if (lk_likely(p)) {
|
||||
if (--p->m_refcount == 0)
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T* retain_unmanaged(T* p) {
|
||||
if (lk_likely(p))
|
||||
p->m_refcount++;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline ConstraintData* kiwi_constraint_new(
|
||||
const KiwiExpression* lhs,
|
||||
const KiwiExpression* rhs,
|
||||
RelationalOperator op,
|
||||
double strength
|
||||
) {
|
||||
if (strength < 0.0) {
|
||||
strength = kiwi::strength::required;
|
||||
}
|
||||
|
||||
std::vector<Term> terms;
|
||||
terms.reserve(static_cast<std::size_t>(
|
||||
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
|
||||
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
|
||||
));
|
||||
|
||||
if (lhs) {
|
||||
for (int i = 0; i < lhs->term_count; ++i) {
|
||||
const auto& t = lhs->terms[i];
|
||||
terms.emplace_back(Variable(t.var), t.coefficient);
|
||||
}
|
||||
}
|
||||
if (rhs) {
|
||||
for (int i = 0; i < rhs->term_count; ++i) {
|
||||
const auto& t = rhs->terms[i];
|
||||
terms.emplace_back(Variable(t.var), -t.coefficient);
|
||||
}
|
||||
}
|
||||
|
||||
return make_unmanaged<ConstraintData>(
|
||||
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
||||
static_cast<RelationalOperator>(op),
|
||||
strength
|
||||
);
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
|
||||
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||
solver.addConstraint(Constraint(c));
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, ConstraintData* constraint) {
|
||||
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||
solver.removeConstraint(Constraint(c));
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, VariableData* var, double strength) {
|
||||
return wrap_err(s, var, [strength](auto&& solver, auto&& v) {
|
||||
solver.addEditVariable(Variable(v), strength);
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, VariableData* var) {
|
||||
return wrap_err(s, var, [](auto&& solver, auto&& v) {
|
||||
solver.removeEditVariable(Variable(v));
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, VariableData* var, double value) {
|
||||
return wrap_err(s, var, [value](auto&& solver, auto&& v) {
|
||||
solver.suggestValue(Variable(v), value);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
||||
|
||||
#endif // LUAKIWI_INT_H_
|
||||
1688
luakiwi/luakiwi.cpp
Normal file
1688
luakiwi/luakiwi.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user