diff --git a/framework/3rd/moon/.gitignore b/framework/3rd/moon/.gitignore new file mode 100644 index 0000000..6e519c8 --- /dev/null +++ b/framework/3rd/moon/.gitignore @@ -0,0 +1,30 @@ +# docco output directory +#docs/ + +# local files for remembering stuff +HISTO +TODO + +# temporary files +.*.swp + +# object files +*.o +*.obj + +# libraries +*.so +*.dll +*.a +*.lib +*.exp + +# executables +*.exe + +# precompiled Lua bytecode files +*.luac + +# LuaRocks packages +*.rock + diff --git a/framework/3rd/moon/README.md b/framework/3rd/moon/README.md new file mode 100644 index 0000000..5847c97 --- /dev/null +++ b/framework/3rd/moon/README.md @@ -0,0 +1,552 @@ +# Moon -- A C Binding Toolkit for Lua # + +This library provides new convenience functions for binding C types to +Lua as userdata for Lua 5.1, Lua 5.2, and Lua 5.3. It supports objects +with different lifetimes, polymorphic type checking, type-safe binding +of tagged unions or embedded structs, properties and methods at the +same time, and uniform handling of pointers to objects using a simple +and small set of API functions. + + +## Using this Library ## + +This package includes a header file `moon.h` and the corresponding +source file `moon.c`. To use this package you need to include the +header file wherever you want to call one of the macros/functions +defined within. If you just have a single source file where you want +to use those functions, you are done: `moon.h` includes `moon.c` and +makes every function `static`. If you have multiple source files that +need functions/macros from this library, this approach is flawed, +because you will end up with multiple versions of the same functions. +Instead include the header wherever you need it, but when you compile +those source files, define the macro `MOON_PREFIX` to a unique name +to use for all functions in the `moon` library. You also have to +compile and link `moon.c` using the same define. This approach will +change the common `moon_` prefix to your custom prefix behind the +scenes to avoid linker errors in case another library also links to +`moon.c`. +The header file `moon_flag.h` can be included whenever needed, but it +depends on the functions defined in `moon.c`. The `moon_dlfix.h` +header is completely independent, but relies on some platform specific +functions. + + +## Reference ## + +This section lists all provided macros/functions. + + +### `moon.h`/`moon.c` ### + +The main part of the moon toolkit. + + +#### `MOON_EXPORT`, `MOON_IMPORT`, `MOON_LOCAL` #### + + #define MOON_EXPORT + #define MOON_IMPORT + #define MOON_LOCAL + +Macros for specifying symbol visibility. + + +#### `MOON_CONCAT` #### + + #define MOON_CONCAT( _a, _b ) + +A macro that evaluates both arguments and joins them together. You can +use that to build valid C identifiers with custom prefixes or +suffixes. + + +#### `MOON_STRINGIFY` #### + + #define MOON_STRINGIFY( _v ) + +A macro that has the same effect as `#_v`, but works outside of a +macro substitution. + + +#### `moon_object_header` #### + + typedef struct { + unsigned char flags; + unsigned char cleanup_offset; + unsigned char vcheck_offset; + unsigned char object_offset; + } moon_object_header; + +Common data structure shared by all userdata objects created via the +moon toolkit. The object may have optional fields following the memory +of this header structure, stored at the given offsets. The `flags` +field is a bit mask describing the details of the object. A pointer to +this header can be obtained by using plain `lua_touserdata` on a moon +object. + + +#### `moon_object_cast` #### + + typedef void* (*moon_object_cast)( void* ); + +Function pointer type for conversion functions used by `moon_defcast`. + + +#### `moon_object_destructor` #### + + typedef void (*moon_object_destructor)( void* ); + +Function pointer type for cleanup functions of moon objects. + + +#### `MOON_OBJECT_IS_VALID`, `MOON_OBJECT_IS_POINTER` #### + + #define MOON_OBJECT_IS_VALID 0x01 + #define MOON_OBJECT_IS_POINTER 0x02 + +Values stored in the `flags` field of the `moon_object_header` +structure. The only value interesting for users of the library is the +`MOON_OBJECT_IS_VALID` flag which is reset automatically by the +`moon_killobject` function to signal that the destructor has already +run. + + +#### `moon_defobject` #### + + /* [ -nup, +0, e ] */ + void moon_defobject( lua_State* L, + char const* metatable_name, + size_t userdata_size, + luaL_Reg const* methods, + int nup ); + +This function creates a new metatable and registers the functions in +the `luaL_Reg` array (functions starting with double underscores `__` +go into the metatable, functions starting with a fullstop `.` are +setup as properties, and the rest goes into a table used by the +`__index` metafield). Property functions act as `__index` and +`__newindex` functions at the same time, i.e. they should return a +value when called with two arguments, and assign the third value when +called with three. If the `luaL_Reg` array also contains an `__index` +and/or `__newindex` function, those functions are called as fallbacks +when method/property lookup has failed. In case a metatable with the +given name already exists, an error is raised. The `userdata_size` is +stored in the metatable for the `moon_newobject` function -- use a +size of 0 to prohibit use of `moon_newobject` (e.g. for incomplete +types). If `nup` is non-zero, it pops those upvalues from the current +Lua stack top and makes them available to all registered functions +(metamethods, property functions, *and* methods). A `__gc` metamethod +and a default `__tostring` metamethod are provided by `moon_defobject` +as well. + + +#### `moon_newobject` #### + + /* [ -0, +1, e ] */ + void* moon_newobject( lua_State* L, + char const* metatable_name, + moon_object_destructor destructor ); + +This function allocates a userdata, sets a metatable, and stores the +cleanup function for later use by the `__gc` metamethod or the +`moon_killobject` function. It throws an error if the `metatable_name` +has not been registered via the `moon_defobject` function. The new +userdata object is pushed to the top of the Lua stack, and a pointer +to the payload (*not* the `moon_object_header` structure) is returned. + + +#### `moon_newpointer` #### + + /* [ -0, +1, e ] */ + void** moon_newpointer( lua_State* L, + char const* metatable_name, + moon_object_destructor destructor ); + +This function allocates a userdata suitable for storing a pointer, +sets a metatable, and stores the cleanup function for later use by the +`__gc` metamethod or the `moon_killobject` function. It is equivalent +to `moon_newobject` with the difference that the payload is stored as +a pointer, not inside the userdata memory. The pointer is initialized +to `NULL` when this function returns, and may be set by assigning to +the memory location pointed to by the return value. + + +#### `moon_newfield` #### + + /* [ -0, +1, e ] */ + void** moon_newfield( lua_State* L, + char const* metatable_name, + int idx, + int (*isvalid)( void* p ), + void* p ); + +This function allocates a userdata suitable for storing a pointer, and +sets a metatable. It is similar to `moon_newpointer`, but it is +intended for exposing a data structure embedded within another +userdata (referenced by stack position `idx`). The resulting moon +object keeps the parent userdata alive by storing a reference in its +uservalue table. If `idx` is `0`, no uservalue table is set. Setting a +cleanup function is not possible, because the parent userdata is +responsible for cleaning up memory and other resources. +If an `isvalid` function pointer is provided, it is called by the +`moon_checkobject`/`moon_testobject` functions to check whether the +object is still valid. This can be used to make sure that a tagged +union used as parent userdata still has the correct type, or that the +parent userdata hasn't released any necessary resources prior to +garbage collection. If the value referenced by `idx` is a moon object +that also has an `isvalid` check registered, all checks are performed +in the order from parent object(s) to child object. + + +#### `moon_getmethods` #### + + /* [ -0, +(0|1), e ] */ + int moon_getmethods( lua_State* L, + char const* tname ); + +If the metatable identified by `tname` has methods registered, pushes +the methods table and returns `LUA_TTABLE`. Otherwise nothing is +pushed and `LUA_TNIL` is returned. This function only works for moon +objects and raises an error if the metatable `tname` wasn't registered +via `moon_defobject`. + + +#### `moon_killobject` #### + + /* [ -0, +0, e ] */ + void moon_killobject( lua_State* L, + int idx ); + +If the moon object at the given stack index is valid, its cleanup +function is run, and the object is marked as invalid (to prevent the +cleanup function from running again). This function can be used to +reclaim resources before the object becomes unreachable. + + +#### `moon_defcast` #### + + /* [ -0, +0, e ] */ + void moon_defcast( lua_State* L, + char const* tname1, + char const* tname2, + moon_object_cast cast ); + +Registers the conversion function `cast` for converting userdata +objects of type `tname1` to type `tname2`. The `cast` function is +called automatically by `moon_checkobject( L, idx, tname2 )` when +applied to an object of type `tname1`, so function implementations can +be reused without extra work. The metatable `tname1` must already +exist and belong to a moon object type (created via `moon_defobject`). + + +#### `moon_checkobject` #### + + /* [ -0, +0, v ] */ + void* moon_checkobject( lua_State* L, + int idx, + char const* metatable_name ); + +This function ensures that the value stored at stack index `idx` + +1. is a full userdata +2. is a moon object +3. has the metatable identified by `metatable_name` or has a + cast function to `metatable_name` registered +4. has the `MOON_OBJECT_IS_VALID` flag set +5. all `isvalid` functions return a non-zero value (for objects + created via `moon_newfield`) +6. contains a non-`NULL` pointer value (for objects created via + `moon_newpointer` or `moon_newfield`). + +If any of those conditions are false, an error is raised. Otherwise +this function returns a pointer to the object's memory (meaning the +objects created via `moon_newpointer` and `moon_newfield` are +dereferenced once, and if necessary the registered cast function is +called). + + +#### `moon_testobject` #### + + /* [ -0, +0, e ] */ + void* moon_testobject( lua_State* L, + int idx, + char const* metatable_name ); + +Performs the same checks as `moon_checkobject`, but returns `NULL` if +any of those conditions are false instead of raising an error. + + +#### `moon_checkint` #### + + /* [ -0, +0, v ] */ + lua_Integer moon_checkint( lua_State* L, + int idx, + lua_Integer min, + lua_Integer max ); + +This function works like `luaL_checkinteger` but additionally ensures +that the given value is in the range [`min`, `max`], or else an error +is thrown. + + +#### `moon_optint` #### + + /* [ -0, +0, v ] */ + lua_Integer moon_optint( lua_State* L, + int idx, + lua_Integer min, + lua_Integer max, + lua_Integer def ); + +Similar to `moon_checkint` but uses the default value `def` if the +value at the given stack position is `nil` or `none`. + + +#### `moon_atexit` #### + + /* [ -0, +1, e ] */ + int* moon_atexit( lua_State* L, + lua_CFunction cleanup ); + +This function puts an integer-sized userdata (initialized to 0) in the +registry and sets the given `cleanup` function as `__gc` metamethod. +The userdata is also pushed to the top of the Lua stack, and returned +as an `int` pointer. +Use this function if you want to call a cleanup function when the Lua +state is closed, but only if some initialization succeeded. The usual +approach is as follows: + +1. Call `moon_atexit` registering your cleanup function. +2. Do your initialization. +3. If successful, you set the `int` pointer to non-zero, which is + almost atomic and can't fail, and pop the userdata. +4. When the cleanup function is called, check for a non-zero value + before actually cleaning up. + + +#### `moon_setuvfield` #### + + /* [ -1, +0, e ] */ + void moon_setuvfield( lua_State* L, + int idx, + char const* key ); + +This function pops the value at the top of the stack and stores it +under `key` in the environment/uservalue table of the object at stack +position `idx`. + + +#### `moon_getuvfield` #### + + /* [ -0, +(0|1), e ] */ + int moon_getuvfield( lua_State* L, + int idx, + char const* key ); + +This function works similar to `luaL_getmetafield`, but it looks for +`key` in the environment/uservalue table of the object at index `idx`, +and pushes the corresponding value to the top of the stack. If there +is no uservalue table or no such field, nothing is pushed and +`LUA_TNIL` is returned. Otherwise, the return value is the type of the +pushed value. + + +#### `moon_getcache` #### + + /* [ -0, +1, e ] */ + void moon_getcache( lua_State* L, + int idx ); + +This function looks up and pushes a weak-valued table under a private +key in the table given by index `idx` (often `LUA_REGISTRYINDEX`). If +the weak-valued table doesn't exist yet, it is created automatically. +This function is useful to map lightuserdata to full userdata, but it +can also be used to cache full userdata for enum values (using +separate caches per enum type), etc. + + +#### `moon_stack_assert` #### + + /* [ -0, +0, v ] */ + void moon_stack_assert( lua_State* L, + ... ); + +This "function" is implemented as a macro that evaluates to `void` if +`NDEBUG` is defined. If it is not, it tries to match the type +specifications (strings) given as arguments to the values at the top +of the Lua stack. Every mismatch is reported on `stderr`, and finally +the whole Lua stack is dumped to `stderr` using the `moon_stack` +function below, and an error is raised. Currently the following type +specifications are supported: + +* `"n"`: nil +* `"b"`: boolean +* `"l"`: lightuserdata +* `"i"`: integer (equivalent to `"d"` before Lua 5.3) +* `"d"`: number (think `double`) +* `"s"`: string +* `"t"`: table +* `"f"`: function +* `"u"`: userdata +* `"c"`: coroutine +* `"a"`: any (non-nil) value + +You can combine the single letter options to express alternatives, so +e.g. `"tf"` means: table or function. + + +#### `moon_stack` #### + + /* [ -0, +0, - ] */ + void moon_stack( lua_State* L ); + +This "function" is also implemented as a macro that evaluates to +`void` in case `NDEBUG` is defined. Otherwise it prints the current +contents of the Lua stack in human-readable format to `stderr`. + + +#### `moon_absindex` #### + +Compatiblity macro for `lua_absindex`, but also available on Lua 5.1 +as a function. + + +#### `moon_derive` #### + + int moon_derive( lua_State* L ); + +A `lua_CFunction` that may be registered as part of your module and +allows Lua code to subclass a moon object. When called it expects two +strings as arguments: a new (non-existing) type name and an old +(existing) type name of a moon object type. It sets up the new type +name as an alias to the old type but with a different methods table. +The new methods table (initially a copy of the methods of the old +type) is returned. + + +#### `moon_downcast` #### + + int moon_downcast( lua_State* L ); + +A `lua_CFunction` that may be registered as part of your module and +allows Lua code to change the type of a moon object to a type created +via `moon_derive`. It is typically used in constructors of derived +types, and expects a moon object and the new type name as arguments. +If successful, the object (with its metatable replaced) is returned. + + +### `moon_flag.h` ### + +`moon_flag.h` is a macro file, that can be included multiple times and +each time defines a new userdata type as a type-safe representation of +a given enum/flag type in Lua. The resulting userdata values support +`+` (bitwise or, in Lua 5.3 also `|`), `-` (create a new value with +certain bits cleared), and calling (test if all bits are set). For Lua +5.3 also bitwise "and" and bitwise "not" are defined. +Parameters are passed as macros before including the macro file. Any +arguments and all internal macros are `#undef`ed before leaving the +macro file. The following parameters are recognized: + +* `MOON_FLAG_NAME` (required): A metatable name used for defining a + userdata type. +* `MOON_FLAG_TYPE` (required): The underlying enum/flag type that is + handled by the custom userdata. +* `MOON_FLAG_SUFFIX` (required): All defined functions have this + suffix (and the `moon_flag_` prefix) to make them unique. +* `MOON_FLAG_NOBITOPS` (optional): If this macro is defined, no + metamethods for bitwise operations are created. This includes + `__add`, `__sub`, and `__call` metamethods. If you do this, you + should think about using strings and `luaL_checkoption` instead of + userdata for representing your flags in Lua. +* `MOON_FLAG_NORELOPS` (optional): If this macro is defined, the + `__eq` metamethod is not created. +* `MOON_FLAG_USECACHE` (optional): The constructor function for this + flag looks in a local cache before creating a new full userdata, + and returns the cached value if possible. This way each enum/flag + value has at most one userdata associated with it. +* `MOON_FLAG_EQMETHOD( _a, _b )` (optional): If you need a custom + comparison operation instead of the usual `==`, define this macro. + +The following (static) functions will be defined, unless they are +disabled via one of the parameter macros above: + + /* [ -0, +0, e ] */ + void moon_flag_def_SUFFIX( lua_State* L ); + /* [ -0, +1, e ] */ + void moon_flag_new_SUFFIX( lua_State* L, TYPE value ); + /* [ -0, +0, v ] */ + TYPE moon_flag_get_SUFFIX( lua_State* L, int idx ); + + int moon_flag_add_SUFFIX( lua_State* L ); + int moon_flag_sub_SUFFIX( lua_State* L ); + int moon_flag_call_SUFFIX( lua_State* L ); + int moon_flag_and_SUFFIX( lua_State* L ); /* Lua 5.3+ */ + int moon_flag_not_SUFFIX( lua_State* L ); /* Lua 5.3+ */ + int moon_flag_eq_SUFFIX( lua_State* L ); + +The last six are metamethods and not supposed to be called from C. +`moon_flag_def_SUFFIX` defines the new type, creates the metatable and +registers all metamethods. `moon_flag_new_SUFFIX` pushes a userdata +representing the given value to the top of the Lua stack, while +`moon_flag_get_SUFFIX` returns the corresponding enum value from a +userdata on the Lua stack (or raises an error). + + +### `moon_dlfix.h` ### + +On Linux and BSDs (and possibly other Unix machines) binary extension +modules aren't linked to the Lua library directly, but instead expect +the main executable to reexport the Lua API. If you don't have control +over the main executable (e.g. you are writing a plugin for a 3rd +party program), you are out of luck. This header file tries to +reexport the Lua API from the shared library linked to your plugin to +make it available for extension modules. It relies on some platform +specific tricks, so it probably won't work everywhere. + + +#### `MOON_DLFIX` #### + + #define MOON_DLFIX() + +This macro uses the dynamic linker to search for the Lua API in an +already loaded shared library and reexport it for extension modules. +If the necessary linker tricks don't work on the given platform, this +macro evaluates to a `void` expression, and you will continue to get +the usual unresolved symbol errors when loading a binary extension +module. + + +#### `MOON_DLFIX_LIBNAME` #### + +If the builtin heuristics fail to find the shared Lua library you can +specify the library name/path by defining this macro. + + +#### `MOON_DLFIX_LIBPREFIX` #### + +For some OSes *all* loaded shared libraries is searched for an ELF +object that contains the Lua symbols. Since this procedure also finds +shared objects that have the Lua library as a dependency, the library +name is checked for a known prefix to make sure that only the actual +Lua library is exported. The default is `"liblua"`, but you can change +it by defining this macro. + + +## Contact ## + +Philipp Janda, siffiejoe(a)gmx.net + +Comments and feedback are always welcome. + + +## License ## + +`moon` is *copyrighted free software* distributed under the Tiny +license. The full license text follows: + + moon (c) 2013-2016 Philipp Janda + + You may do anything with this work that copyright law would normally + restrict, so long as you retain the above notice(s) and this license + in all redistributed copies and derived works. There is no warranty. + + diff --git a/framework/3rd/moon/examples/dlfixex.c b/framework/3rd/moon/examples/dlfixex.c new file mode 100644 index 0000000..e8d620f --- /dev/null +++ b/framework/3rd/moon/examples/dlfixex.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include "plugin.h" + +int main( void ) { + int ret = EXIT_FAILURE; + void* lib = dlopen( "./plugin.so", RTLD_LAZY|RTLD_LOCAL ); + void* sym = NULL; + plugin f; + if( !lib || !(sym = dlsym( lib, PLUGIN )) ) { + fprintf( stderr, "ERROR: %s\n", dlerror() ); + goto error; + } + f = (plugin)sym; + ret = f(); +error: + if( lib ) + dlclose( lib ); + return ret; +} + diff --git a/framework/3rd/moon/examples/flgex.c b/framework/3rd/moon/examples/flgex.c new file mode 100644 index 0000000..04cc310 --- /dev/null +++ b/framework/3rd/moon/examples/flgex.c @@ -0,0 +1,54 @@ +#include +#include +#include "moon.h" + + +#define MOON_FLAG_NAME "X" +#define MOON_FLAG_TYPE unsigned +#define MOON_FLAG_SUFFIX X +#define MOON_FLAG_USECACHE +#include "moon_flag.h" + +#define MOON_FLAG_NAME "Y" +#define MOON_FLAG_TYPE unsigned +#define MOON_FLAG_SUFFIX Y +#include "moon_flag.h" + + +static int flgex_getXmethods( lua_State* L ) { + if( moon_getmethods( L, "X" ) == LUA_TNIL ) + lua_pushnil( L ); + return 1; +} + + +int luaopen_flgex( lua_State* L ) { + luaL_Reg const flgex_funcs[] = { + { "getXmethods", flgex_getXmethods }, + { NULL, NULL } + }; + /* define the flags and create their metatables */ + moon_flag_def_X( L ); + moon_flag_def_Y( L ); +#if LUA_VERSION_NUM < 502 + luaL_register( L, "flgex", flgex_funcs ); +#else + luaL_newlib( L, flgex_funcs ); +#endif + /* create some predefined flags */ + moon_flag_new_X( L, 0 ); + lua_setfield( L, -2, "NULL" ); + moon_flag_new_X( L, 1 ); + lua_setfield( L, -2, "ONE" ); + moon_flag_new_X( L, 2 ); + lua_setfield( L, -2, "TWO" ); + moon_flag_new_Y( L, 4 ); + lua_setfield( L, -2, "THREE" ); + moon_flag_new_Y( L, 8 ); + lua_setfield( L, -2, "FOUR" ); + /* we don't use those functions in this example: */ + (void)moon_flag_get_X; + (void)moon_flag_get_Y; + return 1; +} + diff --git a/framework/3rd/moon/examples/objex.c b/framework/3rd/moon/examples/objex.c new file mode 100644 index 0000000..0c2188c --- /dev/null +++ b/framework/3rd/moon/examples/objex.c @@ -0,0 +1,528 @@ +/* + * Example code for userdata handling in the moon toolkit. + * + * The moon toolkits provides the following functions for handling + * userdata in an easy and safe way: + * - moon_defobject + * - moon_newobject + * - moon_newpointer + * - moon_newfield + * - moon_killobject + * - moon_checkobject + * - moon_testobject + * - moon_defcast + * + * Using those functions enables you to + * - Create and register a new metatable for a C type in a single + * function call, including methods and metamethods with upvalues. + * - Have properties and methods for an object at the same time. + * - Use C values and pointers to those values in a uniform way. + * - Support multiple different ways to create/destroy a C type. + * - Expose fields embedded in another object in a type-safe way. + * - Bind tagged unions in a type-safe way. + * - Define functions that release resources in a safe way before + * the object becomes unreachable. + * - Use userdata polymorphically (use one method implementation for + * multiple similar types). + */ +#include +#include +#include +#include +#include +#include +#include "moon.h" + + +/* Types to be exposed to Lua: */ +typedef struct { + int x; + int y; +} D; + +typedef struct { + D d; +} C; + +typedef struct { + double f; +} B; + +#define TYPE_B 1 +#define TYPE_C 2 + +typedef struct { + int tag; + union { + B b; + C c; + } u; +} A; + + +/* Check functions to make sure that embedded userdata are still + * valid. A change in the tagged union (A_switch) or running a cleanup + * function (C_close) can make embedded userdata invalid. */ +static int type_b_check( void* p ) { + int* tagp = p; + int res = *tagp == TYPE_B; + return res; +} + +static int type_c_check( void* p ) { + int* tagp = p; + int res = *tagp == TYPE_C; + return res; +} + +static int object_valid_check( void* p ) { + unsigned char* flagp = p; + int res = *flagp & MOON_OBJECT_IS_VALID; + return res; +} + + +/* Types that are similar can share one method implementation, but a + * pointer to one type has to be transformed to a pointer to the other + * type. This is probably much more useful when binding C++ libraries + * with proper type hierarchies! */ +static void* C_to_D( void* p ) { + C* c = p; + printf( "casting C to D\n" ); + return &(c->d); +} + + + +/* The (meta-)methods are pretty straightforward. Just use + * `moon_checkobject` instead of `luaL_checkudata`. */ +static int A_index( lua_State* L ) { + A* a = moon_checkobject( L, 1, "A" ); + char const* key = luaL_checkstring( L, 2 ); + if( 0 == strcmp( key, "tag" ) ) { + lua_pushstring( L, a->tag == TYPE_B ? "b" : "c" ); + } else if( 0 == strcmp( key, "b" ) && a->tag == TYPE_B ) { + /* To avoid creating the sub-userdata on every __index access, the + * userdata values are cached in the uservalue field of the parent + * using the same field name as in the struct. */ + if( moon_getuvfield( L, 1, "b" ) == LUA_TNIL ) { + /* Create a new userdata that represents a field in another + * object already exposed to Lua. A gc function is unnecessary + * since the parent userdata already takes care of that. + * However, the parent userdata must be kept alive long enough + * (by putting the value at index 1 into the uservalue table of + * the new userdata), and the new userdata may only be used as + * long as the `a->tag` field satisfies the `type_b_check` + * function! */ + void** p = moon_newfield( L, "B", 1, type_b_check, &(a->tag) ); + /* The userdata stores a pointer to the `a->b` field. */ + *p = &(a->u.b); + lua_pushvalue( L, -1 ); + moon_setuvfield( L, 1, "b" ); + } + } else if( 0 == strcmp( key, "c" ) && a->tag == TYPE_C ) { + if( moon_getuvfield( L, 1, "c" ) == LUA_TNIL ) { + void** p = moon_newfield( L, "C", 1, type_c_check, &(a->tag) ); + *p = &(a->u.c); + lua_pushvalue( L, -1 ); + moon_setuvfield( L, 1, "c" ); + } + } else + lua_pushnil( L ); + return 1; +} + + +static int A_switch( lua_State* L ) { + A* a = moon_checkobject( L, 1, "A" ); + lua_settop( L, 1 ); + if( a->tag == TYPE_B ) { + a->tag = TYPE_C; + a->u.c.d.x = 0; + a->u.c.d.y = 0; + } else { + a->tag = TYPE_B; + a->u.b.f = 0.0; + } + return 1; +} + + +static int A_printme( lua_State* L ) { + A* a = moon_checkobject( L, 1, "A" ); + if( a->tag == TYPE_B ) + printf( "A { u.b.f = %g }\n", a->u.b.f ); + else + printf( "A { u.c.d = { x = %d, y = %d } }\n", + a->u.c.d.x, a->u.c.d.y ); + return 0; +} + + +static int B_property_f( lua_State* L ) { + B* b = moon_checkobject( L, 1, "B" ); + printf( "property{f} B (uv1: %d, uv2: %d)\n", + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + if( lua_gettop( L ) < 3 ) { /* __index */ + lua_pushnumber( L, b->f ); + return 1; + } else { /* __newindex */ + b->f = luaL_checknumber( L, 3 ); + return 0; + } +} + +#if 0 +static int B_index( lua_State* L ) { + B* b = moon_checkobject( L, 1, "B" ); + char const* key = luaL_checkstring( L, 2 ); + printf( "__index B (uv1: %d, uv2: %d)\n", + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + if( 0 == strcmp( key, "f" ) ) { + lua_pushnumber( L, b->f ); + } else + lua_pushnil( L ); + return 1; +} + + +static int B_newindex( lua_State* L ) { + B* b = moon_checkobject( L, 1, "B" ); + char const* key = luaL_checkstring( L, 2 ); + printf( "__newindex B (uv1: %d, uv2: %d)\n", + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + if( 0 == strcmp( key, "f" ) ) + b->f = luaL_checknumber( L, 3 ); + return 0; +} +#endif + + +static int B_printme( lua_State* L ) { + B* b = moon_checkobject( L, 1, "B" ); + printf( "B { f = %g }\n", b->f ); + return 0; +} + + +static int C_index( lua_State* L ) { + C* c = moon_checkobject( L, 1, "C" ); + /* You can get a pointer to the `moon_object_header` structure + * common to all objects created via the moon toolkit by using the + * normal `lua_touserdata` function: */ + moon_object_header* h = lua_touserdata( L, 1 ); + char const* key = luaL_checkstring( L, 2 ); + printf( "__index C (uv1: %d, uv2: %d)\n", + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + if( 0 == strcmp( key, "d" ) ) { + if( moon_getuvfield( L, 1, "d" ) == LUA_TNIL ) { + /* The `object_valid_check` makes sure that the parent object + * has the `MOON_OBJECT_IS_VALID` flag set. This flag is reset + * by the `moon_killobject` function. */ + void** p = moon_newfield( L, "D", 1, object_valid_check, &h->flags ); + *p = &(c->d); + lua_pushvalue( L, -1 ); + moon_setuvfield( L, 1, "d" ); + } + } else + lua_pushnil( L ); + return 1; +} + + +static int C_newindex( lua_State* L ) { + C* c = moon_checkobject( L, 1, "C" ); + char const* key = luaL_checkstring( L, 2 ); + printf( "__newindex C (uv1: %d, uv2: %d)\n", + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + if( 0 == strcmp( key, "d" ) ) { + D* d = moon_checkobject( L, 3, "D" ); + c->d = *d; + } + return 0; +} + + +static int C_close( lua_State* L ) { + /* Use `moon_testobject` here to test it. `moon_checkobject` would + * make more sense in practice! */ + if( !moon_testobject( L, 1, "C" ) ) + luaL_error( L, "need a 'C' object" ); + /* Run cleanup function (if any) to release resources and mark the + * C object as invalid. `moon_checkobject` will raise an error for + * invalid objects. */ + moon_killobject( L, 1 ); + return 0; +} + + +static int C_printme( lua_State* L ) { + C* c = moon_checkobject( L, 1, "C" ); + printf( "C { d = { x = %d, y = %d } } (uv1: %d, uv2: %d)\n", + c->d.x, c->d.y, + (int)lua_tointeger( L, lua_upvalueindex( 1 ) ), + (int)lua_tointeger( L, lua_upvalueindex( 2 ) ) ); + return 0; +} + + +static int D_index( lua_State* L ) { + D* d = moon_checkobject( L, 1, "D" ); + char const* key = luaL_checkstring( L, 2 ); + if( 0 == strcmp( key, "x" ) ) + lua_pushinteger( L, d->x ); + else if( 0 == strcmp( key, "y" ) ) + lua_pushinteger( L, d->y ); + else { +#if LUA_VERSION_NUM < 502 + lua_getfenv( L, 1 ); +#else + lua_getuservalue( L, 1 ); +#endif + lua_pushvalue( L, 2 ); + lua_rawget( L, -2 ); + lua_replace( L, -2 ); + } + return 1; +} + + +static int D_newindex( lua_State* L ) { + D* d = moon_checkobject( L, 1, "D" ); + char const* key = luaL_checkstring( L, 2 ); + if( 0 == strcmp( key, "x" ) ) + d->x = (int)moon_checkint( L, 3, INT_MIN, INT_MAX ); + else if( 0 == strcmp( key, "y" ) ) + d->y = (int)moon_checkint( L, 3, INT_MIN, INT_MAX ); + else { +#if LUA_VERSION_NUM < 502 + lua_getfenv( L, 1 ); +#else + lua_getuservalue( L, 1 ); +#endif + lua_pushvalue( L, 2 ); + lua_pushvalue( L, 3 ); + lua_rawset( L, -3 ); + } + return 0; +} + + +static int D_printme( lua_State* L ) { + D* d = moon_checkobject( L, 1, "D" ); + printf( "D { x = %d, y = %d }\n", d->x, d->y ); + return 0; +} + + +static int D_vcall( lua_State* L ) { + moon_checkobject( L, 1, "D" ); + lua_getfield( L, 1, "func" ); + if( lua_isfunction( L, -1 ) ) { + lua_insert( L, 1 ); + lua_call( L, lua_gettop( L )-1, LUA_MULTRET ); + return lua_gettop( L ); + } + return 0; +} + + +static int objex_getAmethods( lua_State* L ) { + if( moon_getmethods( L, "A" ) == LUA_TNIL ) + lua_pushnil( L ); + return 1; +} + + +static int objex_newA( lua_State* L ) { + /* Create a new A object. The memory is allocated inside the + * userdata when using `moon_newobject`. Here no cleanup function + * is used (0 pointer). */ + A* ud = moon_newobject( L, "A", 0 ); + ud->tag = TYPE_B; + ud->u.b.f = 0.0; + /* `moon_newobject`, and `moon_newpointer` don't allocate a + * uservalue table by default. `moon_newfield` only does if the + * given index is non-zero. If you need a uservalue table (e.g. to + * cache embedded userdatas), you have to add one yourself: */ + lua_newtable( L ); +#if LUA_VERSION_NUM < 502 + lua_setfenv( L, -2 ); +#else + lua_setuservalue( L, -2 ); +#endif + return 1; +} + + +static int objex_newB( lua_State* L ) { + B* b = moon_newobject( L, "B", 0 ); + b->f = 0.0; + return 1; +} + + +static void C_destructor( void* p ) { + printf( "destroying C: %p\n", p ); +} + +static int objex_newC( lua_State* L ) { + /* Create a C object and register a dummy cleanup function (just + * for tests): */ + C* c = moon_newobject( L, "C", C_destructor ); + c->d.x = 0; + c->d.y = 0; + printf( "creating C: %p\n", (void*)c ); + lua_newtable( L ); +#if LUA_VERSION_NUM < 502 + lua_setfenv( L, -2 ); +#else + lua_setuservalue( L, -2 ); +#endif + return 1; +} + + +static int objex_newD( lua_State* L ) { + D* d = moon_newobject( L, "D", 0 ); + d->x = 0; + d->y = 0; + lua_newtable( L ); +#if LUA_VERSION_NUM < 502 + lua_setfenv( L, -2 ); +#else + lua_setuservalue( L, -2 ); +#endif + return 1; +} + + +static int objex_getD( lua_State* L ) { + static D d = { 1, 2 }; + /* `moon_newpointer` without a cleanup function can be used to + * expose a global variable to Lua: */ + void** ud = moon_newpointer( L, "D", 0 ); + *ud = &d; + lua_newtable( L ); +#if LUA_VERSION_NUM < 502 + lua_setfenv( L, -2 ); +#else + lua_setuservalue( L, -2 ); +#endif + return 1; +} + + +static D* newD( int x, int y ) { + D* d = malloc( sizeof( *d ) ); + if( d ) { + printf( "allocating D: %p\n", (void*)d ); + d->x = x; + d->y = y; + } + return d; +} + +static void freeD( void* d ) { + printf( "deallocating D: %p\n", d ); + free( d ); +} + +static int objex_makeD( lua_State* L ) { + int x = (int)moon_checkint( L, 1, INT_MIN, INT_MAX ); + int y = (int)moon_checkint( L, 2, INT_MIN, INT_MAX ); + /* Usually, `moon_newpointer` is used when your API handles memory + * allocation and deallocation for its types: */ + void** p = moon_newpointer( L, "D", freeD ); + /* The cleanup function will only run if the pointer is set to a + * non-NULL value. `moon_checkobject` also will raise an error when + * passed a NULL object! */ + *p = newD( x, y ); + if( !*p ) + luaL_error( L, "memory allocation error" ); + lua_newtable( L ); +#if LUA_VERSION_NUM < 502 + lua_setfenv( L, -2 ); +#else + lua_setuservalue( L, -2 ); +#endif + return 1; +} + + +int luaopen_objex( lua_State* L ) { + luaL_Reg const objex_funcs[] = { + { "getAmethods", objex_getAmethods }, + { "newA", objex_newA }, + { "newB", objex_newB }, + { "newC", objex_newC }, + { "newD", objex_newD }, + { "getD", objex_getD }, + { "makeD", objex_makeD }, + { "derive", moon_derive }, + { "downcast", moon_downcast }, + { NULL, NULL } + }; + /* You put metamethods and normal methods in the same luaL_Reg + * array. `moon_defobject` puts the functions in the right place + * automatically. */ + luaL_Reg const A_methods[] = { + { "__index", A_index }, + { "switch", A_switch }, + { "printme", A_printme }, + { NULL, NULL } + }; + luaL_Reg const B_methods[] = { + { ".f", B_property_f }, +#if 0 + { "__index", B_index }, + { "__newindex", B_newindex }, + { "printme", B_printme }, +#endif + { NULL, NULL } + }; + luaL_Reg const C_methods[] = { + { "__index", C_index }, + { "__newindex", C_newindex }, + { "printme", C_printme }, + /* A C object can be cast to a D object (see below), so C objects + * can use the methods of the D type! */ + { "printmeD", D_printme }, + { "close", C_close }, + { NULL, NULL } + }; + luaL_Reg const D_methods[] = { + { "__index", D_index }, + { "__newindex", D_newindex }, + { "printme", D_printme }, + { "vcall", D_vcall }, + { NULL, NULL } + }; + /* All object types must be defined once (this creates the + * metatables): */ + moon_defobject( L, "A", sizeof( A ), A_methods, 0 ); + (void)B_printme; /* avoid warning */ + lua_pushinteger( L, 1 ); + lua_pushinteger( L, 2 ); + moon_defobject( L, "B", sizeof( B ), B_methods, 2 ); + lua_pushinteger( L, 1 ); + lua_pushinteger( L, 2 ); + moon_defobject( L, "C", sizeof( C ), C_methods, 2 ); + moon_defobject( L, "D", sizeof( D ), D_methods, 0 ); + /* Add a type cast from a C object to the embedded D object. The + * cast is executed automatically during moon_checkobject. */ + moon_defcast( L, "C", "D", C_to_D ); +#if LUA_VERSION_NUM < 502 + luaL_register( L, "objex", objex_funcs ); +#else + luaL_newlib( L, objex_funcs ); +#endif + return 1; +} + + diff --git a/framework/3rd/moon/examples/plugin.c b/framework/3rd/moon/examples/plugin.c new file mode 100644 index 0000000..0113f1b --- /dev/null +++ b/framework/3rd/moon/examples/plugin.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include "plugin.h" +#include "moon_dlfix.h" + +int plugin_main( void ) { + lua_State* L = luaL_newstate(); + luaL_openlibs( L ); +#if 0 + MOON_DLFIX(); +#endif + if( luaL_dofile( L, "test.lua" ) ) { + fprintf( stderr, "%s\n", lua_tostring( L, 1 ) ); + lua_close( L ); + return EXIT_FAILURE; + } + lua_close( L ); + return EXIT_SUCCESS; +} + diff --git a/framework/3rd/moon/examples/plugin.h b/framework/3rd/moon/examples/plugin.h new file mode 100644 index 0000000..64c546f --- /dev/null +++ b/framework/3rd/moon/examples/plugin.h @@ -0,0 +1,8 @@ +#ifndef PLUGIN_H_ +#define PLUGIN_H_ + +#define PLUGIN "plugin_main" +typedef int (*plugin)( void ); + +#endif /* PLUGIN_H_ */ + diff --git a/framework/3rd/moon/examples/sofix.c b/framework/3rd/moon/examples/sofix.c new file mode 100644 index 0000000..0a149de --- /dev/null +++ b/framework/3rd/moon/examples/sofix.c @@ -0,0 +1,17 @@ +#define _GNU_SOURCE +#include "moon_dlfix.h" + +/* declaration of lua_State as an opaque type */ +struct lua_State; +typedef struct lua_State lua_State; + +#ifndef EXPORT +#define EXPORT extern +#endif + +EXPORT int luaopen_sofix( lua_State* L ) { + (void)L; + MOON_DLFIX(); + return 0; +} + diff --git a/framework/3rd/moon/examples/stkex.c b/framework/3rd/moon/examples/stkex.c new file mode 100644 index 0000000..acb0312 --- /dev/null +++ b/framework/3rd/moon/examples/stkex.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include "moon.h" + +static int somefunc( lua_State* L ) { + moon_stack_assert( L, "s", "ds", "t" ); + lua_pushinteger( L, lua_gettop( L ) ); + return 1; +} + +int luaopen_stkex( lua_State* L ) { + luaL_Reg const stkex_funcs[] = { + { "somefunc", somefunc }, + { NULL, NULL } + }; +#if LUA_VERSION_NUM < 502 + luaL_register( L, "stkex", stkex_funcs ); +#else + luaL_newlib( L, stkex_funcs ); +#endif + return 1; +} diff --git a/framework/3rd/moon/examples/test.lua b/framework/3rd/moon/examples/test.lua new file mode 100755 index 0000000..58301be --- /dev/null +++ b/framework/3rd/moon/examples/test.lua @@ -0,0 +1,135 @@ +#!/usr/bin/lua + +package.cpath = "./?.so;../?.so;.\\?.dll;..\\?.dll" +require( "sofix" ) + +local objex = require( "objex" ) +local flgex = require( "flgex" ) +local stkex = require( "stkex" ) + + +print( _VERSION ) +do + print( ("="):rep( 70 ) ) + print( "[ objex test ]" ) + local m = objex.getAmethods() + print( type( m ) ) + if type( m ) == "table" then + for k,v in pairs( m ) do + print( k, v ) + end + end + local a = objex.newA() + print( a, a.tag ) + a:printme() + local b = a.b + print( a.b, b, a.c, b.f ) + b.f = 1.0 + print( pcall( function() b:printme() end ) ) + a:printme() + a:switch() + print( pcall( function() b:printme() end ) ) + print( a, a.tag ) + a:printme() + local c = a.c + print( a.c, c, a.b ) + c:printme() + local d = c.d + print( c.d, d, d.x, d.y ) + d:printme() + d.x = 5 + d.y = 10 + print( d, d.x, d.y ) + d:printme() + a:switch() + print( pcall( d.printme, d ) ) + a:switch() + d:printme() + c:close() + print( pcall( d.printme, d ) ) + local d2 = objex.getD() + print( d2, d2.x, d2.y ) + d2:printme() + local d3 = objex.makeD( 100, 200 ) + print( d3, d3.x, d3.y ) + d3:printme() + local d4 = objex.newD() + print( d4, d4.x, d4.y ) + d4:printme() + local c2 = objex.newC() + c2.d.x = 22 + c2.d.y = 44 + d2.x = 4 + d2.y = 8 + print( c2 ) + c2:printme() + c2.d = d2 + c2:printme() + c2:printmeD() + d2:printme() + local methods = objex.derive( "Derived", "D" ) + function methods:func( ... ) + print( self, self.a, ... ) + end + local function makeDerived() + local d = objex.newD() + d.a = "a" + return objex.downcast( d, "Derived" ) + end + local x = makeDerived() + x.x = 1 + x.y = 2 + x:printme() + x:vcall( 1, 2, 3 ) +end +collectgarbage() + + +do + print( ("="):rep( 70 ) ) + print( "[ flgex test ]" ) + local m = flgex.getXmethods() + print( type( m ) ) + if type( m ) == "table" then + for k,v in pairs( m ) do + print( k, v ) + end + end + local flags = flgex.NULL + flgex.ONE + flgex.TWO + print( flags ) +--[[ + print( "bitops work(1):", flgex.NULL | flgex.ONE | flgex.TWO == flags ) + print( "bitops work(2):", flags & ~flgex.ONE == flgex.TWO ) +--]] + print( "flags contains flgex.ONE?", flags( flgex.ONE ) ) + print( "flags contains flgex.TWO?", flags( flgex.TWO ) ) + flags = flags - flgex.ONE + print( "flags contains flgex.ONE?", flags( flgex.ONE ) ) + print( "flags contains flgex.TWO?", flags( flgex.TWO ) ) + flags = flags - flgex.TWO + print( "flags contains flgex.ONE?", flags( flgex.ONE ) ) + print( "flags contains flgex.TWO?", flags( flgex.TWO ) ) + print( "same and identical:", flags == flgex.NULL, flags, flgex.NULL ) + flags = (flgex.THREE + flgex.FOUR) - flgex.FOUR + print( "same but not identical:", flags == flgex.THREE, flags, flgex.THREE ) + print( "better error message for mismatched types:" ) + print( pcall( function() local wrong = flgex.ONE + flgex.THREE end ) ) +end + + +do + print( ("="):rep( 70 ) ) + print( "[ stkex test ]" ) + print( pcall( stkex.somefunc, "hello", 123, {} ) ) + print() + print( pcall( stkex.somefunc, "hello", "world", {} ) ) + print() + print( pcall( stkex.somefunc, {} ) ) + print() + print( pcall( stkex.somefunc, true ) ) + print() + print( pcall( stkex.somefunc, "hel\001lo\n", nil, {} ) ) + print() + print( pcall( stkex.somefunc, nil, nil, nil ) ) +end + diff --git a/framework/3rd/moon/moon.c b/framework/3rd/moon/moon.c new file mode 100644 index 0000000..a3320f1 --- /dev/null +++ b/framework/3rd/moon/moon.c @@ -0,0 +1,1349 @@ +/* Copyright 2013-2020 Philipp Janda + * + * You may do anything with this work that copyright law would normally + * restrict, so long as you retain the above notice(s) and this license + * in all redistributed copies and derived works. There is no warranty. + */ + +#include +#include +#include +#include +#include +#include +#include "moon.h" + +/* don't compile it again if it's already included via moon.h */ +#ifndef MOON_C_ +#define MOON_C_ + +#if LUA_VERSION_NUM < 502 +MOON_API int moon_absindex(lua_State *L, int i) +{ + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} +#endif + +/* struct that is part of some moon objects (those created via + * `moon_newfield`) and contains a function pointer and a data pointer + * that can be used to check whether the object is still valid. The + * check is done automatically in `moon_{check,test}object`, and is + * primarily needed to safely support tagged unions. */ +typedef struct moon_object_vcheck_ +{ + int (*check)(void *); + void *tagp; + struct moon_object_vcheck_ *next; /* linked list */ +} moon_object_vcheck_; + +/* For keeping memory consumption as low as possible multiple C + * values might be stored side-by-side in the same memory block, and + * we have to figure out the alignment to use for those values. */ +typedef union +{ + lua_Number n; + double d; + lua_Integer i; + long l; + size_t s; + void *p; + void (*fp)(void); +} moon_object_alignment_u_; + +/* Newer versions of C and C++ have a way to figure out alignment + * built into the language, and we use those if available. There's + * also a fallback implementation for older versions. */ +#ifndef MOON_ALIGNOF_ +#if defined(__cplusplus) && __cplusplus >= 201103L +/* alignof is an operator in newer C++ versions */ +#define MOON_ALIGNOF_(_t) alignof(_t) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +/* newer C versions have it in stdalign.h */ +#include +#define MOON_ALIGNOF_(_t) alignof(_t) +#elif defined(__cplusplus) +/* Same as below, but MSVC++ has problems with that code, so we use a + * template instead of a type-as-expression. */ +template +struct moon_alignof_struct_ +{ + char _1; + T _2; +}; +#define MOON_ALIGNOF_(_t) \ + (offsetof(moon_alignof_struct_<_t>, _2) > sizeof(_t) \ + ? sizeof(_t) \ + : offsetof(moon_alignof_struct_<_t>, _2)) +#else +/* Calculate the alignment ourselves. This may give smaller values + * than using `sizeof( _t )` which is also a portable solution. */ +#define MOON_ALIGNOF_(_t) \ + (offsetof( \ + struct \ + { \ + char _1; \ + _t _2; \ + }, \ + _2) > sizeof(_t) \ + ? sizeof(_t) \ + : offsetof( \ + struct \ + { \ + char _1; \ + _t _2; \ + }, \ + _2)) +#endif +#endif + +#define MOON_OBJ_ALIGNMENT_ MOON_ALIGNOF_(moon_object_alignment_u_) +#define MOON_PTR_ALIGNMENT_ MOON_ALIGNOF_(void *) +#define MOON_GCF_ALIGNMENT_ MOON_ALIGNOF_(moon_object_destructor) +#define MOON_VCK_ALIGNMENT_ MOON_ALIGNOF_(moon_object_vcheck_) +#define MOON_ROUNDTO_(_s, _a) ((((_s) + (_a)-1) / (_a)) * (_a)) +#define MOON_PTR_(_p, _o) ((void *)(((char *)(_p)) + (_o))) + +/* Raise properly formatted argument error messages. */ +static int moon_type_error_(lua_State *L, int i, char const *t1, + char const *t2) +{ + char const *msg = lua_pushfstring(L, "%s expected, got %s", t1, t2); + return luaL_argerror(L, i, msg); +} + +static int moon_type_error_invalid_(lua_State *L, int i, + char const *tname) +{ + char const *msg = lua_pushfstring(L, "invalid '%s' object", tname); + return luaL_argerror(L, i, msg); +} + +static int moon_type_error_version_(lua_State *L, int i) +{ + char const *msg = lua_pushfstring(L, "not a moon %d.%d object", + MOON_VERSION_MAJOR, + MOON_VERSION_MINOR); + return luaL_argerror(L, i, msg); +} + +static int moon_is_property(char const *name) +{ + return name[0] == '.'; +} +static int moon_is_meta(char const *name) +{ + return name[0] == '_' && name[1] == '_'; +} +static int moon_is_method(char const *name) +{ + return !moon_is_meta(name) && !moon_is_property(name); +} + +/* Default implementation of a __tostring metamethod for moon objects + * which displays the type name and memory address. */ +MOON_LLINKAGE_BEGIN +static int moon_object_default_tostring_(lua_State *L) +{ + void *ptr = lua_touserdata(L, 1); + char const *name = lua_tostring(L, lua_upvalueindex(1)); + char const *basename = strrchr(name, '.'); + if (basename != NULL) + name = basename + 1; + lua_pushfstring(L, "[%s]: %p", name, ptr); + return 1; +} +MOON_LLINKAGE_END + +/* Run the destructor and mark the object as invalid/destroyed. */ +static void moon_object_run_destructor_(moon_object_header *h) +{ + if (h->cleanup_offset > 0 && (h->flags & MOON_OBJECT_IS_VALID)) + { + void *p = MOON_PTR_(h, h->object_offset); + moon_object_destructor *gc = NULL; + gc = (moon_object_destructor *)MOON_PTR_(h, h->cleanup_offset); + if (h->flags & MOON_OBJECT_IS_POINTER) + p = *((void **)p); + if (*gc != 0 && p != NULL) + (*gc)(p); + } + h->flags &= ~MOON_OBJECT_IS_VALID; +} + +/* Common __gc metamethod for all moon objects. The actual finalizer + * function is stored in the userdata to support different lifetimes. + */ +MOON_LLINKAGE_BEGIN +static int moon_object_default_gc_(lua_State *L) +{ + moon_object_header *h = (moon_object_header *)lua_touserdata(L, 1); + moon_object_run_destructor_(h); + return 0; +} +MOON_LLINKAGE_END + +static int moon_index_dispatch_methods_(lua_State *L) +{ + if (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) + { + lua_pushvalue(L, 2); /* duplicate key */ + lua_rawget(L, lua_upvalueindex(1)); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); + } + return 0; +} + +static int moon_index_dispatch_properties_(lua_State *L) +{ + if (lua_type(L, lua_upvalueindex(2)) == LUA_TTABLE) + { + lua_pushvalue(L, 2); /* duplicate key */ + lua_rawget(L, lua_upvalueindex(2)); + if (!lua_isnil(L, -1)) + { + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; + } + lua_pop(L, 1); + } + return 0; +} + +static int moon_index_dispatch_function_(lua_State *L) +{ + if (lua_type(L, lua_upvalueindex(3)) == LUA_TFUNCTION) + { + lua_pushvalue(L, lua_upvalueindex(3)); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_call(L, 2, 1); + return 1; + } + return 0; +} + +static int moon_newindex_dispatch_properties_(lua_State *L) +{ + if (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) + { + lua_pushvalue(L, 2); /* duplicate key */ + lua_rawget(L, lua_upvalueindex(1)); + if (lua_isfunction(L, -1)) + { + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + lua_call(L, 3, 0); + return 1; + } + lua_pop(L, 1); + } + return 0; +} + +static int moon_newindex_dispatch_function_(lua_State *L) +{ + if (lua_type(L, lua_upvalueindex(2)) == LUA_TFUNCTION) + { + lua_pushvalue(L, lua_upvalueindex(2)); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + lua_call(L, 3, 0); + return 1; + } + return 0; +} + +/* Check the methods and properties tables for a given key and then + * (if unsuccessful) call the registered C function for looking up + * properties. */ +MOON_LLINKAGE_BEGIN +static int moon_index_dispatch_(lua_State *L) +{ + if (!moon_index_dispatch_methods_(L) && + !moon_index_dispatch_properties_(L) && + !moon_index_dispatch_function_(L)) + lua_pushnil(L); + return 1; +} + +static int moon_newindex_dispatch_(lua_State *L) +{ + if (!moon_newindex_dispatch_properties_(L) && + !moon_newindex_dispatch_function_(L)) + luaL_error(L, "attempt to set invalid field"); + return 0; +} +MOON_LLINKAGE_END + +static lua_CFunction moon_getf_(lua_State *L, char const *name, + lua_CFunction def) +{ + lua_CFunction f = 0; + lua_pushliteral(L, "__moon"); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_type(L, -1) != LUA_TTABLE) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushliteral(L, "__moon"); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + lua_getfield(L, -1, name); + f = lua_tocfunction(L, -1); + lua_pop(L, 1); + if (!f) + { + f = def; + lua_pushcfunction(L, def); + lua_setfield(L, -2, name); + } + lua_pop(L, 1); + return f; +} + +static void moon_pushreg_(lua_State *L, luaL_Reg const funcs[], + int (*predicate)(char const *), + int nups, int firstupvalue, int skip) +{ + if (funcs != NULL) + { + lua_newtable(L); + for (; funcs->func; ++funcs) + { + if (predicate(funcs->name)) + { + int i = 0; + for (i = 0; i < nups; ++i) + lua_pushvalue(L, firstupvalue + i); + lua_pushcclosure(L, funcs->func, nups); + lua_setfield(L, -2, funcs->name + skip); + } + } + } + else + lua_pushnil(L); +} + +static void moon_pushfunction_(lua_State *L, lua_CFunction func, + int nups, int firstupvalue) +{ + if (func != 0) + { + int i = 0; + for (i = 0; i < nups; ++i) + lua_pushvalue(L, firstupvalue + i); + lua_pushcclosure(L, func, nups); + } + else + lua_pushnil(L); +} + +/* Depending on the availability of a methods table and/or a C + * function for looking up properties this function creates an __index + * metamethod (function or table). */ +static void moon_makeindex_(lua_State *L, luaL_Reg const methods[], + luaL_Reg const properties[], + lua_CFunction pindex, int nups) +{ + int firstupvalue = lua_gettop(L) + 1 - nups; + if (!properties && !pindex) + { /* methods only (maybe) */ + moon_pushreg_(L, methods, moon_is_method, nups, firstupvalue, 0); + if (nups > 0) + { + lua_replace(L, firstupvalue); + lua_pop(L, nups - 1); + } + } + else if (!methods && !properties) + { /* index function only */ + lua_pushcclosure(L, pindex, nups); + } + else + { + lua_CFunction dispatch = moon_getf_(L, "index", moon_index_dispatch_); + moon_pushreg_(L, methods, moon_is_method, nups, firstupvalue, 0); + moon_pushreg_(L, properties, moon_is_property, nups, firstupvalue, 1); + moon_pushfunction_(L, pindex, nups, firstupvalue); + lua_pushcclosure(L, dispatch, 3); + if (nups > 0) + { + lua_replace(L, firstupvalue); + lua_pop(L, nups - 1); + } + } +} + +static void moon_makenewindex_(lua_State *L, luaL_Reg const properties[], + lua_CFunction pnewindex, int nups) +{ + if (!properties && !pnewindex) + { + lua_pop(L, nups); + lua_pushnil(L); + } + else if (!properties) + { + lua_pushcclosure(L, pnewindex, nups); + } + else + { + int firstupvalue = lua_gettop(L) + 1 - nups; + lua_CFunction dispatch = moon_getf_(L, "newindex", moon_newindex_dispatch_); + moon_pushreg_(L, properties, moon_is_property, nups, firstupvalue, 1); + moon_pushfunction_(L, pnewindex, nups, firstupvalue); + lua_pushcclosure(L, dispatch, 2); + if (nups > 0) + { + lua_replace(L, firstupvalue); + lua_pop(L, nups - 1); + } + } +} + +/* Type names used by the moon toolkit must not start with a double + * underscore, because they might be used as keys in the metatable. */ +static void moon_check_tname_(lua_State *L, char const *tname) +{ + if (tname == NULL || tname[0] == '_') + luaL_error(L, "invalid type name: '%s'", tname ? tname : "NULL"); +} + +MOON_API void moon_defobject(lua_State *L, char const *tname, + size_t sz, luaL_Reg const *methods, + int nups) +{ + int has_methods = 0; + int has_properties = 0; + lua_CFunction index = 0; + lua_CFunction newindex = 0; + moon_check_tname_(L, tname); + luaL_checkstack(L, 2 * nups + 4, "moon_defobject"); + /* we don't use luaL_newmetatable to make sure that we never have a + * half-constructed metatable in the registry! */ + luaL_getmetatable(L, tname); + if (!lua_isnil(L, -1)) + luaL_error(L, "type '%s' is already defined", tname); + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, tname); + lua_pushcclosure(L, moon_object_default_tostring_, 1); + lua_setfield(L, -2, "__tostring"); + if (methods != NULL) + { + luaL_Reg const *l = methods; + int i = 0; + for (; l->func != NULL; ++l) + { + if (moon_is_meta(l->name)) + { + int is_index = 0 == strcmp(l->name + 2, "index"); + int is_newindex = 0 == strcmp(l->name + 2, "newindex"); + if (!is_index && !is_newindex) + { + for (i = 0; i < nups; ++i) + lua_pushvalue(L, -nups - 1); + lua_pushcclosure(L, l->func, nups); + lua_setfield(L, -2, l->name); + } + else if (is_index) /* handle __index later */ + index = l->func; + else /* handle __newindex later */ + newindex = l->func; + } + else if (moon_is_property(l->name)) + { + has_properties = 1; + } + else + has_methods = 1; + } + } + if (has_methods || has_properties || index) + { + int i = 0; + for (i = 0; i < nups; ++i) + lua_pushvalue(L, -nups - 1); + moon_makeindex_(L, has_methods ? methods : NULL, + has_properties ? methods : NULL, index, nups); + lua_setfield(L, -2, "__index"); + } + if (has_properties || newindex) + { + int i = 0; + for (i = 0; i < nups; ++i) + lua_pushvalue(L, -nups - 1); + moon_makenewindex_(L, methods, newindex, nups); + lua_setfield(L, -2, "__newindex"); + } + lua_pushboolean(L, 0); + lua_setfield(L, -2, "__metatable"); + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); + lua_pushcfunction(L, moon_object_default_gc_); + lua_pushvalue(L, -1); + lua_setfield(L, -3, "__gc"); + lua_setfield(L, -2, "__close"); + lua_pushinteger(L, MOON_VERSION); + lua_setfield(L, -2, "__moon_version"); + lua_pushinteger(L, (lua_Integer)sz); + lua_setfield(L, -2, "__moon_size"); + lua_setfield(L, LUA_REGISTRYINDEX, tname); + lua_pop(L, nups); +} + +/* Verify that the value at the stack top is the metatable for a moon + * object type. */ +static void moon_check_metatable_(lua_State *L, char const *tname) +{ + if (!lua_istable(L, -1)) + luaL_error(L, "no metatable for type '%s' defined", tname); + lua_getfield(L, -1, "__moon_version"); + if (lua_tointeger(L, -1) != MOON_VERSION) + luaL_error(L, "'%s' is not a moon %d.%d object type", tname, + MOON_VERSION_MAJOR, MOON_VERSION_MINOR); + lua_pop(L, 1); +} + +/* Pushes the metatable for the given type onto the Lua stack, and + * makes sure that the given type is a moon object type. */ +static void moon_push_metatable_(lua_State *L, char const *tname) +{ + moon_check_tname_(L, tname); + luaL_getmetatable(L, tname); + moon_check_metatable_(L, tname); +} + +MOON_API void *moon_newobject(lua_State *L, char const *tname, + void (*gc)(void *)) +{ + moon_object_header *obj = NULL; + size_t off1 = 0; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4116) +#endif + size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_OBJ_ALIGNMENT_); + size_t sz = 0; + luaL_checkstack(L, 2, "moon_newobject"); + moon_push_metatable_(L, tname); + lua_getfield(L, -1, "__moon_size"); + sz = lua_tointeger(L, -1); + lua_pop(L, 1); + if (sz == 0) + luaL_error(L, "type '%s' is incomplete (size is 0)", tname); + if (gc != 0) + { + off1 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_GCF_ALIGNMENT_); + off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_destructor), + MOON_OBJ_ALIGNMENT_); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + obj = (moon_object_header *)lua_newuserdata(L, sz + off2); + if (off1 > 0) + { + moon_object_destructor *cl = NULL; + cl = (moon_object_destructor *)MOON_PTR_(obj, off1); + *cl = gc; + } + obj->cleanup_offset = off1; + obj->object_offset = off2; + obj->vcheck_offset = 0; + obj->flags = MOON_OBJECT_IS_VALID; + lua_insert(L, -2); + lua_setmetatable(L, -2); + return MOON_PTR_(obj, off2); +} + +MOON_API void **moon_newpointer(lua_State *L, char const *tname, + void (*gc)(void *)) +{ + moon_object_header *obj = NULL; + void **p = NULL; + size_t off1 = 0; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4116) +#endif + size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_PTR_ALIGNMENT_); + luaL_checkstack(L, 2, "moon_newpointer"); + moon_push_metatable_(L, tname); + if (gc != 0) + { + off1 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_GCF_ALIGNMENT_); + off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_destructor), + MOON_PTR_ALIGNMENT_); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + obj = (moon_object_header *)lua_newuserdata(L, sizeof(void *) + off2); + p = (void **)MOON_PTR_(obj, off2); + *p = NULL; + if (off1 > 0) + { + moon_object_destructor *cl = NULL; + cl = (moon_object_destructor *)MOON_PTR_(obj, off1); + *cl = gc; + } + obj->cleanup_offset = off1; + obj->object_offset = off2; + obj->vcheck_offset = 0; + obj->flags = MOON_OBJECT_IS_VALID | MOON_OBJECT_IS_POINTER; + lua_insert(L, -2); + lua_setmetatable(L, -2); + return p; +} + +MOON_API void **moon_newfield(lua_State *L, char const *tname, + int idx, int (*isvalid)(void *), + void *tagp) +{ + moon_object_header *obj = NULL; + moon_object_vcheck_ *nextcheck = NULL; + void **p = NULL; + size_t off1 = 0; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4116) +#endif + size_t off2 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_PTR_ALIGNMENT_); + luaL_checkstack(L, 3, "moon_newfield"); + if (idx != 0) + { + moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx); + idx = moon_absindex(L, idx); + if (h != NULL && lua_getmetatable(L, idx)) + { + lua_getfield(L, -1, "__moon_version"); + if (lua_tointeger(L, -1) == MOON_VERSION) + { + if (h->vcheck_offset > 0) + { + moon_object_vcheck_ *vc = NULL; + vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset); + if (isvalid == 0) + { /* inherit vcheck from idx object */ + isvalid = vc->check; + tagp = vc->tagp; + nextcheck = vc->next; + } + else /* add it to the chain */ + nextcheck = vc; + } + } + lua_pop(L, 2); + } + } + moon_push_metatable_(L, tname); + if (isvalid != 0) + { + off1 = MOON_ROUNDTO_(sizeof(moon_object_header), + MOON_VCK_ALIGNMENT_); + off2 = MOON_ROUNDTO_(off1 + sizeof(moon_object_vcheck_), + MOON_PTR_ALIGNMENT_); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + obj = (moon_object_header *)lua_newuserdata(L, sizeof(void *) + off2); + p = (void **)MOON_PTR_(obj, off2); + *p = NULL; + obj->vcheck_offset = off1; + obj->object_offset = off2; + obj->cleanup_offset = 0; + obj->flags = MOON_OBJECT_IS_VALID | MOON_OBJECT_IS_POINTER; + if (off1 > 0) + { + moon_object_vcheck_ *vc = NULL; + vc = (moon_object_vcheck_ *)MOON_PTR_(obj, off1); + vc->check = isvalid; + vc->tagp = tagp; + vc->next = nextcheck; + } + lua_insert(L, -2); + lua_setmetatable(L, -2); + if (idx != 0) + { + lua_newtable(L); + lua_pushvalue(L, idx); + lua_rawseti(L, -2, 1); +#if LUA_VERSION_NUM < 502 + lua_setfenv(L, -2); +#else + lua_setuservalue(L, -2); +#endif + } + return p; +} + +MOON_API int moon_getmethods(lua_State *L, char const *tname) +{ + int t = 0; + luaL_checkstack(L, 2, "moon_getmethods"); + moon_push_metatable_(L, tname); + lua_getfield(L, -1, "__index"); + lua_replace(L, -2); + t = lua_type(L, -1); + if (t == LUA_TTABLE) + return t; + else if (t == LUA_TFUNCTION) + { + lua_CFunction dispatch = moon_getf_(L, "index", + moon_index_dispatch_); + if (lua_tocfunction(L, -1) == dispatch && + lua_getupvalue(L, -1, 1) != NULL) + { + lua_replace(L, -2); + if (lua_type(L, -1) == LUA_TTABLE) + return LUA_TTABLE; + } + } + lua_pop(L, 1); + return LUA_TNIL; +} + +MOON_API void moon_killobject(lua_State *L, int idx) +{ + moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx); + luaL_checkstack(L, 2, "moon_killobject"); + if (h == NULL || !lua_getmetatable(L, idx)) + moon_type_error_version_(L, idx); + lua_getfield(L, -1, "__moon_version"); + if (lua_tointeger(L, -1) != MOON_VERSION) + moon_type_error_version_(L, idx); + lua_pop(L, 2); + moon_object_run_destructor_(h); +} + +MOON_API void moon_defcast(lua_State *L, char const *tname1, + char const *tname2, + moon_object_cast cast) +{ + luaL_checkstack(L, 2, "moon_defcast"); + moon_push_metatable_(L, tname1); + moon_check_tname_(L, tname2); + lua_pushcfunction(L, (lua_CFunction)(void (*)(void))cast); + lua_setfield(L, -2, tname2); + lua_pop(L, 1); +} + +/* Validates a chain of vcheck objects. The chain won't be long, so + * a recursive approach should be fine! */ +static int moon_validate_vcheck_(moon_object_vcheck_ const *vc) +{ + if (vc->next != NULL) + return moon_validate_vcheck_(vc->next) && + (vc->check == 0 || vc->check(vc->tagp)); + else + return vc->check == 0 || vc->check(vc->tagp); +} + +MOON_API void *moon_checkobject(lua_State *L, int idx, + char const *tname) +{ + moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx); + void *p = NULL; + int res = 0; + moon_object_cast cast = 0; + moon_check_tname_(L, tname); + luaL_checkstack(L, 3, "moon_checkobject"); + idx = moon_absindex(L, idx); + if (h == NULL) + moon_type_error_(L, idx, tname, luaL_typename(L, idx)); + if (lua_islightuserdata(L, idx)) + moon_type_error_(L, idx, tname, "lightuserdata"); + if (!lua_getmetatable(L, idx)) + moon_type_error_(L, idx, tname, "userdata"); + lua_getfield(L, -1, "__moon_version"); + if (lua_tointeger(L, -1) != MOON_VERSION) + { + lua_pop(L, 2); + moon_type_error_version_(L, idx); + } + lua_pop(L, 1); + luaL_getmetatable(L, tname); + res = lua_rawequal(L, -1, -2); + lua_pop(L, 1); + if (!res) + { + lua_getfield(L, -1, tname); + cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1); + lua_pop(L, 1); + if (cast == 0) + { + char const *name = NULL; + lua_getfield(L, -1, "__name"); + name = lua_tostring(L, -1); + moon_type_error_(L, idx, tname, name ? name : "userdata"); + } + } + lua_pop(L, 1); + if (!(h->flags & MOON_OBJECT_IS_VALID)) + moon_type_error_invalid_(L, idx, tname); + if (h->vcheck_offset > 0) + { + moon_object_vcheck_ *vc = NULL; + vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset); + if (!moon_validate_vcheck_(vc)) + moon_type_error_invalid_(L, idx, tname); + } + p = MOON_PTR_(h, h->object_offset); + if (h->flags & MOON_OBJECT_IS_POINTER) + p = *((void **)p); + if (p == NULL) + moon_type_error_invalid_(L, idx, tname); + if (cast != 0) + { + p = cast(p); + if (p == NULL) + moon_type_error_invalid_(L, idx, tname); + } + return p; +} + +MOON_API void *moon_testobject(lua_State *L, int idx, + char const *tname) +{ + moon_object_header *h = (moon_object_header *)lua_touserdata(L, idx); + void *p = NULL; + int res = 0; + moon_object_cast cast = 0; + moon_check_tname_(L, tname); + luaL_checkstack(L, 2, "moon_testobject"); + if (h == NULL || !lua_getmetatable(L, idx)) + return NULL; + lua_getfield(L, -1, "__moon_version"); + if (lua_tointeger(L, -1) != MOON_VERSION) + { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 1); + luaL_getmetatable(L, tname); + res = lua_rawequal(L, -1, -2); + lua_pop(L, 1); + if (!res) + { + lua_getfield(L, -1, tname); + cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1); + lua_pop(L, 2); + if (cast == 0) + return NULL; + } + else + lua_pop(L, 1); + if (!(h->flags & MOON_OBJECT_IS_VALID)) + return NULL; + if (h->vcheck_offset > 0) + { + moon_object_vcheck_ *vc = NULL; + vc = (moon_object_vcheck_ *)MOON_PTR_(h, h->vcheck_offset); + if (!moon_validate_vcheck_(vc)) + return NULL; + } + p = MOON_PTR_(h, h->object_offset); + if (h->flags & MOON_OBJECT_IS_POINTER) + p = *((void **)p); + if (cast != 0) + p = cast(p); + return p; +} + +static void *moon_cast_id_(void *p) +{ + return p; +} + +static void moon_copy_table_(lua_State *L, int src, int dst) +{ + src = moon_absindex(L, src); + dst = moon_absindex(L, dst); + lua_pushnil(L); + while (lua_next(L, src)) + { + lua_pushvalue(L, -2); /* duplicate key */ + lua_pushvalue(L, -2); /* duplicate value */ + lua_rawset(L, dst); + lua_pop(L, 1); /* pop value */ + } +} + +MOON_LLINKAGE_BEGIN +MOON_API int moon_derive(lua_State *L) +{ + char const *newtype = luaL_checkstring(L, 1); + char const *oldtype = luaL_checkstring(L, 2); + int t = 0; + lua_settop(L, 2); + moon_check_tname_(L, newtype); + lua_pushvalue(L, 1); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_argcheck(L, lua_isnil(L, -1), 1, + "attempt to redefine type"); + lua_pop(L, 1); + lua_pushvalue(L, 2); + lua_rawget(L, LUA_REGISTRYINDEX); /* 3: old metatable */ + moon_check_metatable_(L, oldtype); + /* clone metatable */ + lua_newtable(L); /* 4: new metatable */ + moon_copy_table_(L, 3, 4); + /* replace __tostring */ + lua_pushvalue(L, 1); + lua_pushcclosure(L, moon_object_default_tostring_, 1); + lua_setfield(L, 4, "__tostring"); + /* add cast to old type */ + lua_pushvalue(L, 2); + lua_pushcfunction(L, moon_getf_(L, "cast", + (lua_CFunction)(void (*)(void))moon_cast_id_)); + lua_rawset(L, 4); + /* replace __index metamethod */ + lua_pushliteral(L, "__index"); + lua_rawget(L, 4); /* 5: __index metamethod */ + t = lua_type(L, 5); + lua_newtable(L); /* 6: new methods table */ + lua_pushliteral(L, "__index"); /* 7: string "__index" */ + if (t == LUA_TTABLE) + { + moon_copy_table_(L, 5, 6); + lua_pushvalue(L, 6); /* 8: new methods table */ + } + else if (t == LUA_TFUNCTION) + { + lua_CFunction dispatch = moon_getf_(L, "index", + moon_index_dispatch_); + if (lua_tocfunction(L, 5) == dispatch) + { + lua_getupvalue(L, 5, 1); /* 8: old methods table */ + if (lua_istable(L, -1)) + moon_copy_table_(L, -1, 6); + lua_pop(L, 1); + lua_pushvalue(L, 6); /* 8: new methods table */ + lua_getupvalue(L, 5, 2); /* 9: properties */ + lua_getupvalue(L, 5, 3); /* 10: index func */ + lua_pushcclosure(L, dispatch, 3); /* 8: dispatcher */ + } + else + { + lua_pushvalue(L, 6); /* 8: new methods table */ + lua_pushnil(L); /* 9: no properties */ + lua_pushvalue(L, 5); /* 10: index func */ + lua_pushcclosure(L, dispatch, 3); /* 8: dispatcher */ + } + } + else + lua_pushvalue(L, 6); /* 8: new methods table */ + lua_rawset(L, 4); + /* register new type */ + lua_pushvalue(L, 1); + lua_pushvalue(L, 4); + lua_rawset(L, LUA_REGISTRYINDEX); + return 1; +} +MOON_LLINKAGE_END + +MOON_LLINKAGE_BEGIN +MOON_API int moon_downcast(lua_State *L) +{ + void *h = lua_touserdata(L, 1); + char const *tname = luaL_checkstring(L, 2); + moon_object_cast cast = 0, id_cast = 0; + lua_settop(L, 2); + luaL_argcheck(L, h != NULL && lua_getmetatable(L, 1), 1, + "object expected"); /* 3: metatable */ + luaL_argcheck(L, lua_istable(L, -1), 1, "object expected"); + lua_getfield(L, -1, "__moon_version"); + luaL_argcheck(L, lua_tointeger(L, -1) == MOON_VERSION, 1, + "object expected"); + lua_pop(L, 1); + moon_check_tname_(L, tname); + lua_getfield(L, -1, "__name"); /* 4: __name */ + lua_pushvalue(L, 2); + lua_rawget(L, LUA_REGISTRYINDEX); /* 5: metatable */ + moon_check_metatable_(L, tname); + lua_pushvalue(L, 4); + lua_rawget(L, 5); /* 6: cast function */ + cast = (moon_object_cast)(void (*)(void))lua_tocfunction(L, -1); + id_cast = (moon_object_cast)(void (*)(void))moon_getf_(L, "cast", (lua_CFunction)(void (*)(void))moon_cast_id_); + luaL_argcheck(L, cast == id_cast, 1, "invalid downcast"); + lua_pop(L, 1); + lua_setmetatable(L, 1); + lua_settop(L, 1); + return 1; +} +MOON_LLINKAGE_END + +MOON_API lua_Integer moon_checkint(lua_State *L, int idx, + lua_Integer low, + lua_Integer high) +{ + lua_Integer v = luaL_checkinteger(L, idx); + int valid = (high < low) ? (low <= v || v <= high) + : (low <= v && v <= high); + if (!valid) + { + char const *msg = NULL; +#if LUA_VERSION_NUM >= 503 + msg = lua_pushfstring(L, "value out of range [%I,%I]", low, high); +#else + if (low >= INT_MIN && low <= INT_MAX && + high >= INT_MIN && high <= INT_MAX) + msg = lua_pushfstring(L, "value out of range [%d,%d]", + (int)low, (int)high); + else + msg = lua_pushfstring(L, "value out of range [%f,%f]", + (lua_Number)low, (lua_Number)high); +#endif + luaL_argerror(L, idx, msg); + } + return v; +} + +MOON_API lua_Integer moon_optint(lua_State *L, int idx, + lua_Integer low, lua_Integer high, + lua_Integer def) +{ + if (!lua_isnoneornil(L, idx)) + def = moon_checkint(L, idx, low, high); + return def; +} + +MOON_API int *moon_atexit(lua_State *L, lua_CFunction func) +{ + int *flag = NULL; + luaL_checkstack(L, 3, "moon_atexit"); + flag = (int *)lua_newuserdata(L, sizeof(int)); + *flag = 0; + /* setmetatable( flag, { __gc = func } ) */ + lua_newtable(L); + lua_pushcfunction(L, func); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + /* registry[ flag ] = flag */ + lua_pushvalue(L, -1); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + return flag; +} + +MOON_API void moon_setuvfield(lua_State *L, int i, char const *key) +{ + luaL_checkstack(L, 2, "moon_setuvfield"); +#if LUA_VERSION_NUM < 502 + lua_getfenv(L, i); +#else + lua_getuservalue(L, i); +#endif + if (!lua_istable(L, -1)) + luaL_error(L, "attempt to add to non-table uservalue"); + lua_pushvalue(L, -2); + lua_setfield(L, -2, key); + lua_pop(L, 2); +} + +MOON_API int moon_getuvfield(lua_State *L, int i, char const *key) +{ + luaL_checkstack(L, 2, "moon_getuvfield"); +#if LUA_VERSION_NUM < 502 + lua_getfenv(L, i); +#else + lua_getuservalue(L, i); +#endif + if (lua_istable(L, -1)) + { + int t = 0; + lua_getfield(L, -1, key); + lua_replace(L, -2); + t = lua_type(L, -1); + if (t != LUA_TNIL) + return t; + } + lua_pop(L, 1); + return LUA_TNIL; +} + +MOON_API void moon_getcache(lua_State *L, int index) +{ + static char xyz = 0; /* used as a unique key */ + luaL_checkstack(L, 3, "moon_getcache"); + index = moon_absindex(L, index); + lua_pushlightuserdata(L, &xyz); + lua_rawget(L, index); + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "v"); + lua_rawset(L, -3); + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); + lua_pushlightuserdata(L, &xyz); + lua_pushvalue(L, -2); + lua_rawset(L, index); + } +} + +static int moon_check_type_(lua_State *L, int idx, char const *spec) +{ + int t = lua_type(L, idx); + while (*spec != '\0') + { + switch (*spec) + { + case 'n': + if (t == LUA_TNIL) + return 1; + break; + case 'b': + if (t == LUA_TBOOLEAN) + return 1; + break; + case 'l': + if (t == LUA_TLIGHTUSERDATA) + return 1; + break; + case 'i': + if (t == LUA_TNUMBER +#if LUA_VERSION_NUM >= 503 + && lua_isinteger(L, idx) +#endif + ) + return 1; + break; + case 'd': + if (t == LUA_TNUMBER) + return 1; + break; + case 's': + if (t == LUA_TSTRING) + return 1; + break; + case 't': + if (t == LUA_TTABLE) + return 1; + break; + case 'f': + if (t == LUA_TFUNCTION) + return 1; + break; + case 'u': + if (t == LUA_TUSERDATA) + return 1; + break; + case 'c': + if (t == LUA_TTHREAD) + return 1; + break; + case 'a': + if (t != LUA_TNIL) + return 1; + break; + } + ++spec; + } + return 0; +} + +static char const *moon_type2spec_(int t) +{ + switch (t) + { + case LUA_TNONE: + case LUA_TNIL: + return "n"; + case LUA_TBOOLEAN: + return "b"; + case LUA_TLIGHTUSERDATA: + return "l"; + case LUA_TNUMBER: + return "d"; + case LUA_TSTRING: + return "s"; + case LUA_TTABLE: + return "t"; + case LUA_TFUNCTION: + return "f"; + case LUA_TUSERDATA: + return "u"; + case LUA_TTHREAD: + return "c"; + default: + return "?"; + } +} + +#define MOON_SEP_ \ + "######################################################################" + +MOON_API void moon_stack_assert_(lua_State *L, char const *file, + int line, char const *func, ...) +{ + int top = lua_gettop(L); + int nargs = 0; + int skip = 0; + int have_error = 0; + int i = 0; + char const *arg; + va_list ap; + va_start(ap, func); + while ((arg = va_arg(ap, char const *)) != NULL) + ++nargs; + va_end(ap); + if (nargs > top) + { + fputs(MOON_SEP_ "\n", stderr); + fprintf(stderr, "Lua stack assertion failed:" + " less stack slots (%d) than expected (%d)!\n", + top, nargs); + have_error = 1; + skip = nargs - top; + } + va_start(ap, func); + for (i = 0; i < skip; ++i) + arg = va_arg(ap, char const *); + for (i = top + skip - nargs + 1; i <= top; ++i) + { + arg = va_arg(ap, char const *); + if (!moon_check_type_(L, i, arg)) + { + if (!have_error) + fputs(MOON_SEP_ "\n", stderr); + fprintf(stderr, "Lua stack assertion failed:" + " '%s' at slot %d (expected %s'%s')!\n", + moon_type2spec_(lua_type(L, i)), i, + (arg[0] != '\0' && arg[1] == 0) ? "" : "one of ", + arg); + have_error = 1; + } + } + va_end(ap); + if (have_error) + { + moon_stack_(L, file, line, func); + luaL_error(L, "Lua stack assertion failed!"); + } +} + +MOON_API void moon_stack_(lua_State *L, char const *file, int line, + char const *func) +{ + int n = lua_gettop(L); + int i = 0; + fputs(MOON_SEP_ "\n", stderr); + fprintf(stderr, "Lua stack in function '%s' at '%s:%d':\n", + func != NULL ? func : "(unknown)", file, line); + for (i = 1; i <= n; ++i) + { + fprintf(stderr, "%02d: %s ", i, luaL_typename(L, i)); + switch (lua_type(L, i)) + { + case LUA_TNONE: + fputs("(none)\n", stderr); + break; + case LUA_TNIL: + fputs("(nil)\n", stderr); + break; + case LUA_TBOOLEAN: + fprintf(stderr, "(%s)\n", lua_toboolean(L, i) ? "true" : "false"); + break; + case LUA_TNUMBER: +#if LUA_VERSION_NUM >= 503 + if (lua_isinteger(L, i)) + fprintf(stderr, "(" LUA_INTEGER_FMT ")\n", + lua_tointeger(L, i)); + else +#endif + fprintf(stderr, "(" LUA_NUMBER_FMT ")\n", + lua_tonumber(L, i)); + break; + case LUA_TSTRING: + { + char const *str = lua_tostring(L, i); + char const *ellipsis = ""; +#if LUA_VERSION_NUM < 502 + unsigned len = (unsigned)lua_objlen(L, i); +#else + unsigned len = (unsigned)lua_rawlen(L, i); +#endif + unsigned i = 0; + fprintf(stderr, "(len: %u, str: \"", len); + if (len > 15) + { + len = 15; + ellipsis = "..."; + } + for (i = 0; i < len; ++i) + { + if (isprint((unsigned char)str[i])) + putc((unsigned char)str[i], stderr); + else + fprintf(stderr, "\\x%02X", (unsigned)str[i]); + } + fputs(ellipsis, stderr); + fputs("\")\n", stderr); + break; + } + case LUA_TLIGHTUSERDATA: + fprintf(stderr, "(%p)\n", lua_touserdata(L, i)); + break; + case LUA_TUSERDATA: + if (luaL_getmetafield(L, i, "__name")) + { + char const *name = lua_tostring(L, -1); + fprintf(stderr, "(type: \"%s\", size: %u, ptr: %p)\n", + name != NULL ? name : "(NULL)", +#if LUA_VERSION_NUM < 502 + (unsigned)lua_objlen(L, i), +#else + (unsigned)lua_rawlen(L, i), +#endif + lua_touserdata(L, i)); + lua_pop(L, 1); + } + else + { + fprintf(stderr, "(size: %u, ptr: %p)\n", +#if LUA_VERSION_NUM < 502 + (unsigned)lua_objlen(L, i), +#else + (unsigned)lua_rawlen(L, i), +#endif + lua_touserdata(L, i)); + } + break; + default: /* thread, table, function */ + fprintf(stderr, "(%p)\n", lua_topointer(L, i)); + break; + } + } + fputs(MOON_SEP_ "\n", stderr); +} + +#undef MOON_SEP_ + +/* clean up internally used macros */ +#undef MOON_ALIGNOF_ +#undef MOON_OBJ_ALIGNMENT_ +#undef MOON_PTR_ALIGNMENT_ +#undef MOON_GCF_ALIGNMENT_ +#undef MOON_VCK_ALIGNMENT_ +#undef MOON_ROUNDTO_ +#undef MOON_PTR_ + +#endif /* MOON_C_ */ diff --git a/framework/3rd/moon/moon.h b/framework/3rd/moon/moon.h new file mode 100644 index 0000000..051c358 --- /dev/null +++ b/framework/3rd/moon/moon.h @@ -0,0 +1,237 @@ +/* Copyright 2013-2016 Philipp Janda + * + * You may do anything with this work that copyright law would normally + * restrict, so long as you retain the above notice(s) and this license + * in all redistributed copies and derived works. There is no warranty. + */ + +#ifndef MOON_H_ +#define MOON_H_ + +/* file: moon.h + * Utility functions/macros for binding C code to Lua. + */ + +#include +#if defined(__cplusplus) && !defined(MOON_LUA_CPP) +extern "C" +{ +#endif +#include +#include +#if defined(__cplusplus) && !defined(MOON_LUA_CPP) +} +#define MOON_LLINKAGE_BEGIN \ + extern "C" \ + { +#define MOON_LLINKAGE_END } +#endif + +/* use default linkage for functions to pass to Lua */ +#ifndef MOON_LLINKAGE_BEGIN +#define MOON_LLINKAGE_BEGIN +#define MOON_LLINKAGE_END +#endif + +#define MOON_VERSION (300) +#define MOON_VERSION_MAJOR (MOON_VERSION / 100) +#define MOON_VERSION_MINOR (MOON_VERSION - (MOON_VERSION_MAJOR * 100)) + +/* fake CLANG feature detection on other compilers */ +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +/* platform independent attributes for exporting/importing functions + * from shared libraries */ +#if defined(_WIN32) && !defined(__CYGWIN__) +#ifndef MOON_LOCAL +#define MOON_LOCAL +#endif +#ifndef MOON_EXPORT +#define MOON_EXPORT __declspec(dllexport) +#endif +#ifndef MOON_IMPORT +#define MOON_IMPORT __declspec(dllimport) +#endif +#elif (defined(__GNUC__) && __GNUC__ >= 4) || __has_attribute(__visibility__) +#ifndef MOON_LOCAL +#define MOON_LOCAL __attribute__((__visibility__("hidden"))) +#endif +#ifndef MOON_EXPORT +#define MOON_EXPORT __attribute__((__visibility__("default"))) +#endif +#ifndef MOON_IMPORT +#define MOON_IMPORT __attribute__((__visibility__("default"))) +#endif +#endif + +#ifndef MOON_LOCAL +#define MOON_LOCAL +#endif + +#ifndef MOON_EXPORT +#define MOON_EXPORT +#endif + +#ifndef MOON_IMPORT +#define MOON_IMPORT +#endif + +/* handle linking tricks for moon toolkit */ +#if defined(MOON_PREFIX) +/* - change the symbol names of functions to avoid linker conflicts + * - moon.c needs to be compiled (and linked) separately + */ +#if !defined(MOON_API) +/* the following is fine for static linking moon.c to your C module */ +#define MOON_API MOON_LOCAL +#endif +#undef MOON_INCLUDE_SOURCE +#else /* MOON_PREFIX */ +/* - make all functions static and include the source (moon.c) + * - don't change the symbol names of functions + * - moon.c doesn't need to be compiled (and linked) separately + */ +#define MOON_PREFIX moon +#undef MOON_API +#if defined(__GNUC__) || __has_attribute(__unused__) +#define MOON_API __attribute__((__unused__)) static +#else +#define MOON_API static +#endif +#define MOON_INCLUDE_SOURCE +#endif /* MOON_PREFIX */ + +/* some helper macros */ +#define MOON_CONCAT_HELPER(a, b) a##b +#define MOON_CONCAT(a, b) MOON_CONCAT_HELPER(a, b) +#define MOON_STRINGIFY_HELPER(a) #a +#define MOON_STRINGIFY(a) MOON_STRINGIFY_HELPER(a) + +/* make sure all functions can be called using the moon_ prefix, even + * if we change the prefix behind the scenes */ +#define moon_defobject MOON_CONCAT(MOON_PREFIX, _defobject) +#define moon_newobject MOON_CONCAT(MOON_PREFIX, _newobject) +#define moon_newpointer MOON_CONCAT(MOON_PREFIX, _newpointer) +#define moon_newfield MOON_CONCAT(MOON_PREFIX, _newfield) +#define moon_getmethods MOON_CONCAT(MOON_PREFIX, _getmethods) +#define moon_killobject MOON_CONCAT(MOON_PREFIX, _killobject) +#define moon_defcast MOON_CONCAT(MOON_PREFIX, _defcast) +#define moon_checkobject MOON_CONCAT(MOON_PREFIX, _checkobject) +#define moon_testobject MOON_CONCAT(MOON_PREFIX, _testobject) +#define moon_derive MOON_CONCAT(MOON_PREFIX, _derive) +#define moon_downcast MOON_CONCAT(MOON_PREFIX, _downcast) +#define moon_checkint MOON_CONCAT(MOON_PREFIX, _checkint) +#define moon_optint MOON_CONCAT(MOON_PREFIX, _optint) +#define moon_atexit MOON_CONCAT(MOON_PREFIX, _atexit) +#define moon_setuvfield MOON_CONCAT(MOON_PREFIX, _setuvfield) +#define moon_getuvfield MOON_CONCAT(MOON_PREFIX, _getuvfield) +#define moon_getcache MOON_CONCAT(MOON_PREFIX, _getcache) +#define moon_stack_ MOON_CONCAT(MOON_PREFIX, _stack_) +#define moon_stack_assert_ MOON_CONCAT(MOON_PREFIX, _stack_assert_) + +/* all objects defined via moon_defobject share a common header of the + * following type: */ +typedef struct +{ + unsigned char flags; + unsigned char cleanup_offset; + unsigned char vcheck_offset; + unsigned char object_offset; +} moon_object_header; + +/* flag values in moon_object_header: */ +#define MOON_OBJECT_IS_VALID 0x01u +#define MOON_OBJECT_IS_POINTER 0x02u + +/* function pointer type for "casts" */ +typedef void *(*moon_object_cast)(void *); + +/* function pointer type for destructors */ +typedef void (*moon_object_destructor)(void *); + +/* additional Lua API functions in this toolkit */ +MOON_API void moon_defobject(lua_State *L, char const *tname, + size_t sz, luaL_Reg const *methods, + int nup); +MOON_API void *moon_newobject(lua_State *L, char const *tname, + moon_object_destructor destructor); +MOON_API void **moon_newpointer(lua_State *L, char const *tname, + moon_object_destructor destructor); +MOON_API void **moon_newfield(lua_State *L, char const *tname, + int idx, int (*isvalid)(void *p), + void *p); +MOON_API int moon_getmethods(lua_State *L, char const *tname); +MOON_API void moon_killobject(lua_State *L, int idx); +MOON_API void moon_defcast(lua_State *L, char const *tname1, + char const *tname2, + moon_object_cast cast); +MOON_API void *moon_checkobject(lua_State *L, int idx, + char const *tname); +MOON_API void *moon_testobject(lua_State *L, int idx, + char const *tname); + +MOON_LLINKAGE_BEGIN +MOON_API int moon_derive(lua_State *L); +MOON_API int moon_downcast(lua_State *L); +MOON_LLINKAGE_END + +MOON_API lua_Integer moon_checkint(lua_State *L, int idx, + lua_Integer low, + lua_Integer high); +MOON_API lua_Integer moon_optint(lua_State *L, int idx, + lua_Integer low, lua_Integer high, + lua_Integer def); +MOON_API int *moon_atexit(lua_State *L, lua_CFunction func); +MOON_API int moon_getuvfield(lua_State *L, int i, char const *key); +MOON_API void moon_setuvfield(lua_State *L, int i, char const *key); +MOON_API void moon_getcache(lua_State *L, int index); +MOON_API void moon_stack_(lua_State *L, char const *file, int line, + char const *func); +MOON_API void moon_stack_assert_(lua_State *L, char const *file, + int line, char const *func, ...); + +/* some debugging macros */ +#ifndef NDEBUG +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + defined(__GNUC__) || defined(__clang__) +#define moon_stack_assert(L, ...) \ + moon_stack_assert_(L, __FILE__, __LINE__, __func__, __VA_ARGS__, (char const *)NULL) +#define moon_stack(L) \ + moon_stack_(L, __FILE__, __LINE__, __func__) +#elif defined(_MSC_VER) && _MSC_VER >= 1100L +#define moon_stack_assert(L, ...) \ + moon_stack_assert_(L, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__, (char const *)NULL) +#define moon_stack(L) \ + moon_stack_(L, __FILE__, __LINE__, __FUNCTION__) +#else +#error preprocessor does not support variadic macros +#endif +#else +#define moon_stack(L) ((void)(0)) +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + (defined(_MSC_VER) && _MSC_VER >= 1100L) || \ + defined(__GNUC__) || defined(__clang__) +#define moon_stack_assert(...) ((void)0) +#else +#error preprocessor does not support variadic macros +#endif +#endif + +/* Lua version compatibility is out of scope for this library, so only + * compatibility functions needed for the implementation are provided. + * Consider using the Compat-5.3 project which provides backports of + * many Lua 5.3 C API functions for Lua 5.1 and 5.2. + */ +#if LUA_VERSION_NUM < 502 +MOON_API int moon_absindex(lua_State *L, int idx); +#else +#define moon_absindex(L, i) lua_absindex((L), (i)) +#endif + +#if defined(MOON_INCLUDE_SOURCE) +#include "moon.c" +#endif + +#endif /* MOON_H_ */ diff --git a/framework/3rd/moon/moon_dlfix.h b/framework/3rd/moon/moon_dlfix.h new file mode 100644 index 0000000..0158336 --- /dev/null +++ b/framework/3rd/moon/moon_dlfix.h @@ -0,0 +1,183 @@ +/* Copyright 2013-2016 Philipp Janda + * + * You may do anything with this work that copyright law would normally + * restrict, so long as you retain the above notice(s) and this license + * in all redistributed copies and derived works. There is no warranty. + */ + +#ifndef MOON_DLFIX_H_ +#define MOON_DLFIX_H_ + +/* Provides a C function macro that tries to reopen the Lua library + * in global mode, so that C extension modules do not have to be + * relinked for Lua VMs in shared libraries. + */ + +/* enable/disable debug output */ +#if 0 +#include +#define MOON_DLFIX_DBG(_a) (printf _a) +#else +#define MOON_DLFIX_DBG(_a) ((void)0) +#endif + +/* detect some form of UNIX, so that unistd.h can be included to + * use other feature macros */ +#if defined(unix) || defined(__unix) || defined(__unix__) || \ + defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ + (defined(__APPLE__) && defined(__MACH__)) || \ + defined(HAVE_UNISTD_H) + +#include +/* check for minimum POSIX version that specifies dlopen etc. */ +#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || \ + defined(HAVE_DLFCN_H) + +#include +/* check for the existence of the RTLD_NOLOAD flag + * - GLIBC has it (since 2.2) + * - FreeBSD has it + * - ... + */ +#ifdef RTLD_NOLOAD + +/* Lua VM library names to try + * Most of them require the dev-package to be installed, + * but guessing the real library names for multiple distros + * is pretty difficult ... + * If the library is not in the default search path you can + * set LD_LIBRARY_PATH, or use MOON_DLFIX_LIBNAME to specify + * an absolute path to the library. + */ +static char const *const moon_dlfix_lib_names[] = { +/* you can define your own custom name via compiler define: */ +#ifdef MOON_DLFIX_LIBNAME + MOON_DLFIX_LIBNAME, +#endif /* custom Lua library name */ + "liblua5.4.so", /* Lua 5.4, Debian/Ubuntu naming */ + "liblua5.4.so.0", /* same with common SONAME */ + "liblua54.so", + "liblua5.3.so", /* Lua 5.3, Debian/Ubuntu naming */ + "liblua5.3.so.0", /* same with common SONAME */ + "liblua53.so", + "liblua5.2.so", /* Lua 5.2, Debian/Ubuntu naming */ + "liblua5.2.so.0", /* same with common SONAME */ + "liblua52.so", + "liblua.so", /* default name from Lua's makefile */ + "liblua5.1.so", /* Lua 5.1, Debian/Ubuntu naming */ + "liblua5.1.so.0", /* same with common SONAME */ + "liblua51.so", + "libluajit-5.1.so", /* LuaJIT default name */ + "libluajit-5.1.so.2", /* common SONAME */ +}; + +/* On some OSes we can iterate over all loaded libraries to + * find the Lua shared object. + */ +#if defined(__linux__) && defined(_GNU_SOURCE) +#define MOON_DLFIX_DL_ITERATE_PHDR +#include +#endif /* Linux with dl_iterate_phdr() function */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) +#define MOON_DLFIX_DL_ITERATE_PHDR +#include +#endif /* BSDs with dl_iterate_phdr() function */ + +#if !defined(MOON_DLFIX_FIND) && \ + defined(MOON_DLFIX_DL_ITERATE_PHDR) +#include + +#ifndef MOON_DLFIX_LIBPREFIX +#define MOON_DLFIX_LIBPREFIX "liblua" +#endif + +static int moon_dlfix_cb(struct dl_phdr_info *info, size_t size, + void *data) +{ + int *found = data; + void *dl = dlopen(info->dlpi_name, RTLD_LAZY); + (void)size; + MOON_DLFIX_DBG(("Checking ELF object '%s'.\n", info->dlpi_name)); + if (dl) + { + if (dlsym(dl, "lua_gettop")) + { + MOON_DLFIX_DBG(("'%s' does have Lua symbols.\n", info->dlpi_name)); + /* the Lua API could be in a dependency, so test the library + * name for "liblua" */ + char const *libname = strrchr(info->dlpi_name, '/'); + if (libname) + ++libname; /* skip slash */ + else + libname = info->dlpi_name; + if (0 == strncmp(libname, MOON_DLFIX_LIBPREFIX, + sizeof(MOON_DLFIX_LIBPREFIX) - 1)) + { + void *dl2 = dlopen(info->dlpi_name, + RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD); + if (dl2) + { + MOON_DLFIX_DBG(("Found and fixed Lua SO!\n")); + dlclose(dl2); + *found = 1; + } + } + } + dlclose(dl); + } + return *found; +} + +static int moon_dlfix_find(void) +{ + int found = 0; + MOON_DLFIX_DBG(("Iterating all loaded ELF objects ...\n")); + return dl_iterate_phdr(moon_dlfix_cb, &found); +} +#define MOON_DLFIX_FIND() (moon_dlfix_find()) +#endif /* has dl_iterate_phdr() function */ + +#if !defined(MOON_DLFIX_FIND) +/* dummy definition (always returns failure) */ +#define MOON_DLFIX_FIND() (0) +#endif + +/* Try to iterate all loaded shared libraries using a platform- + * specific way to find a loaded Lua shared library. + * If that fails, try a list of common library names. + * In all cases reopen the Lua library using RTLD_GLOBAL and + * RTLD_NOLOAD. + */ +#define MOON_DLFIX() \ + do \ + { \ + if (!MOON_DLFIX_FIND()) \ + { \ + unsigned i = 0; \ + MOON_DLFIX_DBG(("Trying some common Lua library names ...\n")); \ + for (; i < sizeof(moon_dlfix_lib_names) / sizeof(*moon_dlfix_lib_names); ++i) \ + { \ + void *dl = dlopen(moon_dlfix_lib_names[i], RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD); \ + MOON_DLFIX_DBG(("Trying '%s'.\n", moon_dlfix_lib_names[i])); \ + if (dl) \ + { \ + MOON_DLFIX_DBG(("Fixed Lua SO.\n")); \ + dlclose(dl); \ + break; \ + } \ + } \ + } \ + } while (0) + +#endif /* has RTLD_NOLOAD */ +#endif /* has dlfcn.h */ +#endif /* has unistd.h */ + +/* define fallback */ +#ifndef MOON_DLFIX +#define MOON_DLFIX() \ + (MOON_DLFIX_DBG(("moon_dlfix functionality not available!\n"))) +#endif + +#endif /* MOON_DLFIX_H_ */ diff --git a/framework/3rd/moon/moon_flag.h b/framework/3rd/moon/moon_flag.h new file mode 100644 index 0000000..664484e --- /dev/null +++ b/framework/3rd/moon/moon_flag.h @@ -0,0 +1,160 @@ +/* Copyright 2013-2015 Philipp Janda + * + * You may do anything with this work that copyright law would normally + * restrict, so long as you retain the above notice(s) and this license + * in all redistributed copies and derived works. There is no warranty. + */ + +/* this include file is a macro file which could be included + * multiple times with different settings. + */ +#include "moon.h" + +/* "parameter checking" */ +#ifndef MOON_FLAG_NAME +#error MOON_FLAG_NAME is not defined +#endif + +#ifndef MOON_FLAG_TYPE +#error MOON_FLAG_TYPE is not defined +#endif + +#ifndef MOON_FLAG_SUFFIX +#error MOON_FLAG_SUFFIX is not defined +#endif + +#define MOON_FLAG_ADD MOON_CONCAT(moon_flag_add_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_SUB MOON_CONCAT(moon_flag_sub_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_CALL MOON_CONCAT(moon_flag_call_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_AND MOON_CONCAT(moon_flag_and_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_NOT MOON_CONCAT(moon_flag_not_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_EQ MOON_CONCAT(moon_flag_eq_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_DEF MOON_CONCAT(moon_flag_def_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_NEW MOON_CONCAT(moon_flag_new_, MOON_FLAG_SUFFIX) +#define MOON_FLAG_GET MOON_CONCAT(moon_flag_get_, MOON_FLAG_SUFFIX) +#ifndef MOON_FLAG_EQMETHOD +#define MOON_FLAG_EQMETHOD(a, b) ((a) == (b)) +#endif + +static void MOON_FLAG_NEW(lua_State *L, MOON_FLAG_TYPE v) +{ +#ifdef MOON_FLAG_USECACHE + luaL_checkstack(L, 5, MOON_STRINGIFY(MOON_FLAG_NEW)); + luaL_getmetatable(L, MOON_FLAG_NAME); + if (!lua_istable(L, -1)) + luaL_error(L, "no metatable for type '%s' defined", MOON_FLAG_NAME); + moon_getcache(L, -1); + lua_pushnumber(L, (lua_Number)v); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); +#endif + *(MOON_FLAG_TYPE *)moon_newobject(L, MOON_FLAG_NAME, 0) = v; +#ifdef MOON_FLAG_USECACHE + lua_pushnumber(L, (lua_Number)v); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + lua_replace(L, -3); + lua_pop(L, 1); +#endif +} + +static MOON_FLAG_TYPE MOON_FLAG_GET(lua_State *L, int index) +{ + return *(MOON_FLAG_TYPE *)moon_checkobject(L, index, MOON_FLAG_NAME); +} + +#ifndef MOON_FLAG_NOBITOPS +MOON_LLINKAGE_BEGIN +static int MOON_FLAG_ADD(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_TYPE *b = (MOON_FLAG_TYPE *)moon_checkobject(L, 2, MOON_FLAG_NAME); + MOON_FLAG_NEW(L, (MOON_FLAG_TYPE)(*a | *b)); + return 1; +} +static int MOON_FLAG_SUB(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_TYPE *b = (MOON_FLAG_TYPE *)moon_checkobject(L, 2, MOON_FLAG_NAME); + MOON_FLAG_NEW(L, (MOON_FLAG_TYPE)(*a & ~(*b))); + return 1; +} +static int MOON_FLAG_CALL(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_TYPE *b = (MOON_FLAG_TYPE *)moon_checkobject(L, 2, MOON_FLAG_NAME); + lua_pushboolean(L, !(~(*a) & (*b))); + return 1; +} +#if LUA_VERSION_NUM > 502 +static int MOON_FLAG_AND(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_TYPE *b = (MOON_FLAG_TYPE *)moon_checkobject(L, 2, MOON_FLAG_NAME); + MOON_FLAG_NEW(L, (MOON_FLAG_TYPE)(*a & *b)); + return 1; +} +static int MOON_FLAG_NOT(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_NEW(L, (MOON_FLAG_TYPE)(~*a)); + return 1; +} +#endif +MOON_LLINKAGE_END +#endif + +#ifndef MOON_FLAG_NORELOPS +MOON_LLINKAGE_BEGIN +static int MOON_FLAG_EQ(lua_State *L) +{ + MOON_FLAG_TYPE *a = (MOON_FLAG_TYPE *)moon_checkobject(L, 1, MOON_FLAG_NAME); + MOON_FLAG_TYPE *b = (MOON_FLAG_TYPE *)moon_checkobject(L, 2, MOON_FLAG_NAME); + lua_pushboolean(L, MOON_FLAG_EQMETHOD(*a, *b)); + return 1; +} +MOON_LLINKAGE_END +#endif + +static void MOON_FLAG_DEF(lua_State *L) +{ + luaL_Reg const methods[] = { +#ifndef MOON_FLAG_NOBITOPS + {"__add", MOON_FLAG_ADD}, + {"__sub", MOON_FLAG_SUB}, + {"__call", MOON_FLAG_CALL}, +#if LUA_VERSION_NUM > 502 + {"__band", MOON_FLAG_AND}, + {"__bor", MOON_FLAG_ADD}, + {"__bnot", MOON_FLAG_NOT}, +#endif +#endif +#ifndef MOON_FLAG_NORELOPS + {"__eq", MOON_FLAG_EQ}, +#endif + {NULL, NULL} + }; + moon_defobject(L, MOON_FLAG_NAME, sizeof(MOON_FLAG_TYPE), + methods, 0); +} + +#undef MOON_FLAG_ADD +#undef MOON_FLAG_SUB +#undef MOON_FLAG_CALL +#undef MOON_FLAG_AND +#undef MOON_FLAG_NOT +#undef MOON_FLAG_EQ +#undef MOON_FLAG_NEW +#undef MOON_FLAG_DEF +#undef MOON_FLAG_GET + +#undef MOON_FLAG_NAME +#undef MOON_FLAG_TYPE +#undef MOON_FLAG_SUFFIX +#undef MOON_FLAG_NOBITOPS +#undef MOON_FLAG_NORELOPS +#undef MOON_FLAG_USECACHE +#undef MOON_FLAG_EQMETHOD diff --git a/framework/lualib-src/lua-ecs/luaecs.c b/framework/lualib-src/lua-ecs/luaecs.c index fd9bbe9..1ff96fc 100644 --- a/framework/lualib-src/lua-ecs/luaecs.c +++ b/framework/lualib-src/lua-ecs/luaecs.c @@ -12,14 +12,16 @@ #define MAX_COMPONENT 256 #define ENTITY_REMOVED 0 #define DEFAULT_SIZE 128 +#define STRIDE_TAG 0 #define STRIDE_LUA -1 +#define STRIDE_ORDER -2 #define DUMMY_PTR (void *)(uintptr_t)(~0) +#define REARRANGE_THRESHOLD 0x80000000 struct component_pool { int cap; int n; - int count; int stride; // -1 means lua object int last_lookup; unsigned int *id; @@ -29,8 +31,6 @@ struct component_pool struct entity_world { unsigned int max_id; - unsigned int wrap_begin; - int wrap; struct component_pool c[MAX_COMPONENT]; }; @@ -40,7 +40,6 @@ init_component_pool(struct entity_world *w, int index, int stride, int opt_size) struct component_pool *c = &w->c[index]; c->cap = opt_size; c->n = 0; - c->count = 0; c->stride = stride; c->id = NULL; if (stride > 0) @@ -148,27 +147,28 @@ lcollect_memory(lua_State *L) } static int -add_component_id_(lua_State *L, struct entity_world *w, int cid, unsigned int eid) +add_component_id_(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid) { struct component_pool *pool = &w->c[cid]; int cap = pool->cap; int index = pool->n; + assert(pool->stride != STRIDE_ORDER); if (pool->n == 0) { if (pool->id == NULL) { pool->id = (unsigned int *)lua_newuserdatauv(L, cap * sizeof(unsigned int), 0); - lua_setiuservalue(L, 1, cid * 2 + 1); + lua_setiuservalue(L, world_index, cid * 2 + 1); } if (pool->buffer == NULL) { pool->buffer = lua_newuserdatauv(L, cap * pool->stride, 0); - lua_setiuservalue(L, 1, cid * 2 + 2); + lua_setiuservalue(L, world_index, cid * 2 + 2); } else if (pool->stride == STRIDE_LUA) { lua_newtable(L); - lua_setiuservalue(L, 1, cid * 2 + 2); + lua_setiuservalue(L, world_index, cid * 2 + 2); } } else if (pool->n >= pool->cap) @@ -176,14 +176,14 @@ add_component_id_(lua_State *L, struct entity_world *w, int cid, unsigned int ei // expand pool int newcap = cap * 3 / 2; unsigned int *newid = (unsigned int *)lua_newuserdatauv(L, newcap * sizeof(unsigned int), 0); - lua_setiuservalue(L, 1, cid * 2 + 1); + lua_setiuservalue(L, world_index, cid * 2 + 1); memcpy(newid, pool->id, cap * sizeof(unsigned int)); pool->id = newid; int stride = pool->stride; if (stride > 0) { void *newbuffer = lua_newuserdatauv(L, newcap * stride, 0); - lua_setiuservalue(L, 1, cid * 2 + 2); + lua_setiuservalue(L, world_index, cid * 2 + 2); memcpy(newbuffer, pool->buffer, cap * stride); pool->buffer = newbuffer; } @@ -200,15 +200,15 @@ get_ptr(struct component_pool *c, int index) if (c->stride > 0) return (void *)((char *)c->buffer + c->stride * index); else - return c->buffer; + return DUMMY_PTR; } static void * -add_component_(lua_State *L, struct entity_world *w, int cid, unsigned int eid, const void *buffer) +add_component_(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid, const void *buffer) { - int index = add_component_id_(L, w, cid, eid); + int index = add_component_id_(L, world_index, w, cid, eid); struct component_pool *pool = &w->c[cid]; - assert(pool->stride != STRIDE_LUA); + assert(pool->stride >= 0); void *ret = get_ptr(pool, index); if (buffer) memcpy(ret, buffer, pool->stride); @@ -236,7 +236,7 @@ ladd_component(lua_State *L) int stride = w->c[cid].stride; if (stride <= 0) { - int index = add_component_id_(L, w, cid, eid); + int index = add_component_id_(L, 1, w, cid, eid); if (stride == STRIDE_LUA) { // lua object @@ -258,7 +258,7 @@ ladd_component(lua_State *L) { return luaL_error(L, "Invalid data (size=%d/%d) for type %d", (int)sz, stride, cid); } - add_component_(L, w, cid, eid, buffer); + add_component_(L, 1, w, cid, eid, buffer); } return 0; } @@ -268,60 +268,60 @@ lnew_entity(lua_State *L) { struct entity_world *w = getW(L); unsigned int eid = ++w->max_id; - if (eid == 0) - { - assert(w->wrap == 0); - w->wrap = 1; - } + assert(eid != 0); lua_pushinteger(L, eid); return 1; } static void -remove_entity_(struct entity_world *w, int cid, int index, void *L) +insert_id(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid) { struct component_pool *c = &w->c[cid]; - if (index < 0 || index >= c->count) - luaL_error((lua_State *)L, "Invalid index %d/%d", index, c->count); - unsigned int eid = c->id[index]; - add_component_id_((lua_State *)L, w, ENTITY_REMOVED, eid); -} - -static int -lremove_entity(lua_State *L) -{ - struct entity_world *w = getW(L); - int cid = check_cid(L, w, 2); - int index = luaL_checkinteger(L, 3); - remove_entity_(w, cid, index, (void *)L); - return 0; -} - -static int -id_comp(const void *a, const void *b) -{ - const unsigned int ai = *(const unsigned int *)a; - const unsigned int bi = *(const unsigned int *)b; - - if (ai < bi) - return -1; - else if (ai > bi) - return 1; - else - return 0; + assert(c->stride == STRIDE_TAG); + int from = 0; + int to = c->n; + while (from < to) + { + int mid = (from + to) / 2; + int aa = c->id[mid]; + if (aa == eid) + return; + else if (aa < eid) + { + from = mid + 1; + } + else + { + to = mid; + } + } + // insert eid at [from] + if (from < c->n - 1) + { + int i; + // Any dup id ? + for (i = from; i < c->n - 1; i++) + { + if (c->id[i] == c->id[i + 1]) + { + memmove(c->id + from + 1, c->id + from, sizeof(unsigned int) * (i - from)); + c->id[from] = eid; + return; + } + } + } + add_component_id_(L, world_index, w, cid, eid); + memmove(c->id + from + 1, c->id + from, sizeof(unsigned int) * (c->n - from - 1)); + c->id[from] = eid; } -static int -lsort_removed(lua_State *L) +static void +entity_enable_tag_(struct entity_world *w, int cid, int index, int tag_id, void *L, int world_index) { - struct entity_world *w = getW(L); - struct component_pool *pool = &w->c[ENTITY_REMOVED]; - if (pool->n == 0) - return 0; - qsort(pool->id, pool->n, sizeof(unsigned int), id_comp); - pool->count = pool->n; - lua_pushinteger(L, pool->n); - return 1; + struct component_pool *c = &w->c[cid]; + assert(index >= 0 && index < c->n); + unsigned int eid = c->id[index]; + insert_id((lua_State *)L, world_index, w, tag_id, eid); } static int @@ -350,16 +350,16 @@ binary_search(unsigned int *a, int from, int to, unsigned int v) static inline int lookup_component(struct component_pool *pool, unsigned int eid, int guess_index) { - int n = pool->count; + int n = pool->n; if (n == 0) return -1; if (guess_index + GUESS_RANGE >= n) - return binary_search(pool->id, 0, pool->count, eid); + return binary_search(pool->id, 0, pool->n, eid); unsigned int *a = pool->id; int higher = a[guess_index + GUESS_RANGE]; if (eid > higher) { - return binary_search(a, guess_index + GUESS_RANGE + 1, pool->count, eid); + return binary_search(a, guess_index + GUESS_RANGE + 1, pool->n, eid); } int lower = a[guess_index]; if (eid < lower) @@ -369,6 +369,57 @@ lookup_component(struct component_pool *pool, unsigned int eid, int guess_index) return binary_search(a, guess_index, guess_index + GUESS_RANGE, eid); } +static inline void +replace_id(struct component_pool *c, int index, unsigned int eid) +{ + unsigned int rid = c->id[index]; + c->id[index] = eid; + int i; + for (i = index + 1; i < c->n && c->id[i] == rid; i++) + { + c->id[i] = eid; + } +} + +static void +entity_disable_tag_(struct entity_world *w, int cid, int index, int tag_id) +{ + struct component_pool *c = &w->c[cid]; + assert(index >= 0 && index < c->n); + unsigned int eid = c->id[index]; + if (cid != tag_id) + { + c = &w->c[tag_id]; + index = lookup_component(c, eid, c->last_lookup); + if (index < 0) + return; + } + int i; + for (i = index - 1; i >= 0; i--) + { + if (c->id[i] != eid) + { + replace_id(c, i + 1, c->id[i]); + return; + } + } + for (i = index + 1; i < c->n; i++) + { + if (c->id[i] != eid) + { + replace_id(c, index, c->id[i]); + return; + } + } + c->n = 0; +} + +static void +entity_remove_(struct entity_world *w, int cid, int index, void *L, int world_index) +{ + entity_enable_tag_(w, cid, index, ENTITY_REMOVED, L, world_index); +} + struct rearrange_context { struct entity_world *w; @@ -385,7 +436,7 @@ find_min(struct rearrange_context *ctx) for (i = 1; i < MAX_COMPONENT; i++) { int index = ctx->ptr[i - 1]; - if (index < w->c[i].count) + if (index < w->c[i].n) { if (w->c[i].id[index] <= m) { @@ -410,7 +461,7 @@ rearrange(struct entity_world *w) { int index = ctx.ptr[cid - 1]; unsigned int current_id = w->c[cid].id[index]; - // printf("arrange %d -> %d\n", new_id, w->c[cid].id[index]); + // printf("arrange %d <- %d\n", new_id, w->c[cid].id[index]); w->c[cid].id[index] = new_id; if (current_id != last_id) { @@ -419,17 +470,7 @@ rearrange(struct entity_world *w) } ++ctx.ptr[cid - 1]; } - int i, j; - for (i = 1; i < MAX_COMPONENT; i++) - { - struct component_pool *pool = &w->c[i]; - for (j = pool->count; j < pool->n; j++) - { - // printf("arrange new %d -> %d\n", pool->id[j], new_id + pool->id[j] - w->wrap_begin -1); - pool->id[j] = new_id + pool->id[j] - w->wrap_begin - 1; - } - } - w->max_id = new_id + w->max_id - w->wrap_begin - 1; + w->max_id = new_id; } static inline void @@ -467,20 +508,36 @@ static void remove_all(lua_State *L, struct component_pool *pool, struct component_pool *removed, int cid) { int index = 0; - int i; - unsigned int *id = removed->id; - unsigned int last_id = 0; int count = 0; - for (i = 0; i < removed->n; i++) + int i; + if (pool->stride != STRIDE_ORDER) + { + unsigned int *id = removed->id; + unsigned int last_id = 0; + for (i = 0; i < removed->n; i++) + { + if (id[i] != last_id) + { + // todo : order + int r = lookup_component(pool, id[i], index); + if (r >= 0) + { + index = r; + pool->id[r] = 0; + ++count; + } + } + } + } + else { - if (id[i] != last_id) + unsigned int *id = pool->id; + for (i = 0; i < pool->n; i++) { - int r = lookup_component(pool, id[i], index); + int r = lookup_component(removed, id[i], 0); if (r >= 0) { - index = r; - assert(pool->id[r] == id[i]); - pool->id[r] = 0; + id[i] = 0; ++count; } } @@ -488,11 +545,12 @@ remove_all(lua_State *L, struct component_pool *pool, struct component_pool *rem if (count > 0) { index = 0; - if (pool->stride == STRIDE_LUA) + switch (pool->stride) { + case STRIDE_LUA: if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) { - luaL_error(L, "Missing lua object table for type %d", id); + luaL_error(L, "Missing lua object table for type %d", cid); } for (i = 0; i < pool->n; i++) { @@ -503,9 +561,9 @@ remove_all(lua_State *L, struct component_pool *pool, struct component_pool *rem } } lua_pop(L, 1); // pop lua object table - } - else if (pool->stride == 0) - { + break; + case STRIDE_TAG: + case STRIDE_ORDER: for (i = 0; i < pool->n; i++) { if (pool->id[i] != 0) @@ -514,9 +572,8 @@ remove_all(lua_State *L, struct component_pool *pool, struct component_pool *rem ++index; } } - } - else - { + break; + default: for (i = 0; i < pool->n; i++) { if (pool->id[i] != 0) @@ -525,9 +582,9 @@ remove_all(lua_State *L, struct component_pool *pool, struct component_pool *rem ++index; } } + break; } pool->n -= count; - pool->count -= count; } } @@ -548,22 +605,32 @@ lupdate(lua_State *L) remove_all(L, pool, removed, i); } removed->n = 0; - removed->count = 0; } - if (w->wrap) + if (w->max_id > REARRANGE_THRESHOLD) { rearrange(w); - w->wrap = 0; } - w->wrap_begin = w->max_id; - // add componets - for (i = 1; i < MAX_COMPONENT; i++) + + return 0; +} + +static void +remove_dup(struct component_pool *c, int index) +{ + int i; + unsigned int eid = c->id[index]; + int to = index; + for (i = index + 1; i < c->n; i++) { - struct component_pool *c = &w->c[i]; - c->count = c->n; + if (c->id[i] != eid) + { + eid = c->id[i]; + c->id[to] = eid; + ++to; + } } - return 0; + c->n = to; } static void * @@ -571,17 +638,57 @@ entity_iter_(struct entity_world *w, int cid, int index) { struct component_pool *c = &w->c[cid]; assert(index >= 0); - if (index >= c->count) + if (index >= c->n) return NULL; + if (c->stride == STRIDE_TAG) + { + // it's a tag + unsigned int eid = c->id[index]; + if (index > 0 && eid == c->id[index - 1]) + { + remove_dup(c, index); + if (index >= c->n) + return NULL; + } + return DUMMY_PTR; + } return get_ptr(c, index); } +static void * +entity_iter_lua_(struct entity_world *w, int cid, int index, void *L) +{ + void *ret = entity_iter_(w, cid, index); + if (ret != DUMMY_PTR) + return ret; + if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) + { + lua_pop(L, 1); + return 0; + } + int t = lua_rawgeti(L, -1, index + 1); + switch (t) + { + case LUA_TSTRING: + ret = (void *)lua_tostring(L, -1); + break; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + ret = (void *)lua_touserdata(L, -1); + break; + default: + ret = NULL; + break; + } + lua_pop(L, 2); + return ret; +} + static void entity_clear_type_(struct entity_world *w, int cid) { struct component_pool *c = &w->c[cid]; c->n = 0; - c->count = 0; } static int @@ -597,10 +704,11 @@ static void * entity_sibling_(struct entity_world *w, int cid, int index, int slibling_id) { struct component_pool *c = &w->c[cid]; - if (index < 0 || index >= c->count) + if (index < 0 || index >= c->n) return NULL; unsigned int eid = c->id[index]; c = &w->c[slibling_id]; + assert(c->stride != STRIDE_ORDER); int result_index = lookup_component(c, eid, c->last_lookup); if (result_index >= 0) { @@ -614,10 +722,11 @@ static int entity_sibling_index_(struct entity_world *w, int cid, int index, int slibling_id) { struct component_pool *c = &w->c[cid]; - if (index < 0 || index >= c->count) + if (index < 0 || index >= c->n) return 0; unsigned int eid = c->id[index]; c = &w->c[slibling_id]; + assert(c->stride != STRIDE_ORDER); int result_index = lookup_component(c, eid, c->last_lookup); if (result_index >= 0) { @@ -628,31 +737,66 @@ entity_sibling_index_(struct entity_world *w, int cid, int index, int slibling_i } static void * -entity_add_sibling_(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L) +entity_add_sibling_(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L, int world_index) { struct component_pool *c = &w->c[cid]; - assert(index >= 0 && index < c->count); + assert(index >= 0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ - assert(c->stride != STRIDE_LUA); - void *ret = add_component_((lua_State *)L, w, slibling_id, eid, buffer); - c->count = c->n; + assert(c->stride >= 0); + void *ret = add_component_((lua_State *)L, world_index, w, slibling_id, eid, buffer); + c = &w->c[slibling_id]; return ret; } static int -entity_add_sibling_index_(lua_State *L, struct entity_world *w, int cid, int index, int slibling_id) +entity_add_sibling_index_(lua_State *L, int world_index, struct entity_world *w, int cid, int index, int slibling_id) { struct component_pool *c = &w->c[cid]; - assert(index >= 0 && index < c->count); + assert(index >= 0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ assert(c->stride == STRIDE_LUA); - int ret = add_component_id_(L, w, slibling_id, eid); - c->count = c->n; + int ret = add_component_id_(L, world_index, w, slibling_id, eid); return ret; } +static int +comp_index(void *v, const void *a, const void *b) +{ + const unsigned int *aa = (const unsigned int *)a; + const unsigned int *bb = (const unsigned int *)b; + int *vv = (int *)v; + return vv[*aa] - vv[*bb]; +} + +static void +entity_sort_key_(struct entity_world *w, int orderid, int cid, void *L, int world_index) +{ + struct component_pool *c = &w->c[cid]; + assert(c->stride == sizeof(int)); + struct component_pool *order = &w->c[orderid]; + assert(order->stride == STRIDE_ORDER); + + if (order->id == NULL || order->cap < c->cap) + { + order->cap = c->cap; + order->id = (unsigned int *)lua_newuserdatauv(L, c->cap * sizeof(unsigned int), 0); + lua_setiuservalue(L, world_index, orderid * 2 + 1); + } + int i; + for (i = 0; i < c->n; i++) + { + order->id[i] = i; + } + qsort_s(order->id, c->n, sizeof(unsigned int), comp_index, c->buffer); + for (i = 0; i < c->n; i++) + { + order->id[i] = c->id[order->id[i]]; + } + order->n = c->n; +} + static int lcontext(lua_State *L) { @@ -668,6 +812,8 @@ lcontext(lua_State *L) size_t sz = sizeof(struct ecs_context) + sizeof(int) * n; struct ecs_context *ctx = (struct ecs_context *)lua_newuserdatauv(L, sz, 1); ctx->L = (void *)lua_newthread(L); + lua_pushvalue(L, 1); + lua_xmove(L, ctx->L, 1); // put world in the index 1 of newthread lua_setiuservalue(L, -2, 1); ctx->max_id = n; ctx->world = w; @@ -676,6 +822,11 @@ lcontext(lua_State *L) entity_clear_type_, entity_sibling_, entity_add_sibling_, + entity_remove_, + entity_enable_tag_, + entity_disable_tag_, + entity_sort_key_, + entity_iter_lua_, }; ctx->api = &c_api; ctx->cid[0] = ENTITY_REMOVED; @@ -691,9 +842,6 @@ lcontext(lua_State *L) int cid = ctx->cid[i]; if (cid == ENTITY_REMOVED || cid < 0 || cid >= MAX_COMPONENT) return luaL_error(L, "Invalid id (%d) at index %d", cid, i); - struct component_pool *c = &w->c[cid]; - if (c->stride == STRIDE_LUA) - return luaL_error(L, "Can't iterate lua component in C (%d)", cid); } return 1; } @@ -853,6 +1001,7 @@ get_len(lua_State *L, int index) #define COMPONENT_OUT 2 #define COMPONENT_OPTIONAL 4 #define COMPONENT_OBJECT 8 +#define COMPONENT_EXIST 16 struct group_key { @@ -865,6 +1014,8 @@ struct group_key static inline int is_temporary(int attrib) { + if (attrib & COMPONENT_EXIST) + return 0; return (attrib & COMPONENT_IN) == 0 && (attrib & COMPONENT_OUT) == 0; } @@ -884,11 +1035,13 @@ get_write_component(lua_State *L, int lua_index, const char *name, struct field { case LUA_TNIL: lua_pop(L, 1); - // restore cache - lua_getmetatable(L, lua_index); - lua_getfield(L, -1, name); - lua_setfield(L, lua_index, name); - lua_pop(L, 1); // pop metatable + // restore cache (metatable can be absent during sync) + if (lua_getmetatable(L, lua_index)) + { + lua_getfield(L, -1, name); + lua_setfield(L, lua_index, name); + lua_pop(L, 1); // pop metatable + } return 0; case LUA_TTABLE: return 1; @@ -908,7 +1061,7 @@ get_write_component(lua_State *L, int lua_index, const char *name, struct field } static void -write_component_in_field(lua_State *L, int lua_index, const char *name, int n, struct field *f, void *buffer) +write_component_object(lua_State *L, int n, struct field *f, void *buffer) { if (f->key == NULL) { @@ -921,79 +1074,136 @@ write_component_in_field(lua_State *L, int lua_index, const char *name, int n, s } } +static int +remove_tag(lua_State *L, int lua_index, const char *name) +{ + int r = 0; + switch (lua_getfield(L, lua_index, name)) + { + case LUA_TNIL: + r = 1; + break; + case LUA_TBOOLEAN: + r = !lua_toboolean(L, -1); + break; + default: + return luaL_error(L, "Invalid tag type %s", lua_typename(L, lua_type(L, -1))); + } + return r; +} + static void update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { int i; int mainkey = iter->k[0].id; struct component_pool *c = &iter->world->c[mainkey]; - if ((iter->k[0].attrib & COMPONENT_OUT) && get_write_component(L, lua_index, iter->k[0].name, iter->f, c)) + if (!(iter->k[0].attrib & COMPONENT_EXIST)) { - struct component_pool *c = &iter->world->c[mainkey]; - if (c->count <= idx) - { - luaL_error(L, "Can't find component %s for index %d", iter->k[0].name, idx); - } - if (c->stride == STRIDE_LUA) + if (c->stride == STRIDE_TAG) { - if (lua_getiuservalue(L, world_index, mainkey * 2 + 2) != LUA_TTABLE) + // It's a tag + if ((iter->k[0].attrib & COMPONENT_OUT) && remove_tag(L, lua_index, iter->k[0].name)) { - luaL_error(L, "Missing lua table for %d", mainkey); + entity_disable_tag_(iter->world, mainkey, idx, mainkey); } - lua_insert(L, -2); - lua_rawseti(L, -2, idx + 1); - } - else - { - void *buffer = get_ptr(c, idx); - write_component_in_field(L, lua_index, iter->k[0].name, iter->k[0].field_n, iter->f, buffer); } - } - - struct field *f = iter->f + iter->k[0].field_n; - - for (i = 1; i < iter->nkey; i++) - { - struct group_key *k = &iter->k[i]; - struct component_pool *c = &iter->world->c[k->id]; - if ((k->attrib & COMPONENT_OUT) && get_write_component(L, lua_index, k->name, f, c)) + else if ((iter->k[0].attrib & COMPONENT_OUT) && get_write_component(L, lua_index, iter->k[0].name, iter->f, c)) { - int index = entity_sibling_index_(iter->world, mainkey, idx, k->id); - if (index == 0) + struct component_pool *c = &iter->world->c[mainkey]; + if (c->n <= idx) { - luaL_error(L, "Can't find sibling %s of %s", k->name, iter->k[0].name); + luaL_error(L, "Can't find component %s for index %d", iter->k[0].name, idx); } if (c->stride == STRIDE_LUA) { - if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) + if (lua_getiuservalue(L, world_index, mainkey * 2 + 2) != LUA_TTABLE) { - luaL_error(L, "Missing lua table for %d", k->id); + luaL_error(L, "Missing lua table for %d", mainkey); } lua_insert(L, -2); - lua_rawseti(L, -2, index); + lua_rawseti(L, -2, idx + 1); } else { - void *buffer = get_ptr(c, index - 1); - write_component_in_field(L, lua_index, k->name, k->field_n, f, buffer); + void *buffer = get_ptr(c, idx); + write_component_object(L, iter->k[0].field_n, iter->f, buffer); } } - else if (is_temporary(k->attrib) && get_write_component(L, lua_index, k->name, f, c)) + } + + struct field *f = iter->f + iter->k[0].field_n; + + for (i = 1; i < iter->nkey; i++) + { + struct group_key *k = &iter->k[i]; + if (!(k->attrib & COMPONENT_EXIST)) { - if (c->stride == STRIDE_LUA) + struct component_pool *c = &iter->world->c[k->id]; + if (c->stride == STRIDE_TAG) { - int index = entity_add_sibling_index_(L, iter->world, mainkey, idx, k->id); - if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) + // It's a tag + if ((k->attrib & COMPONENT_OUT) || is_temporary(k->attrib)) { - luaL_error(L, "Missing lua table for %d", k->id); + switch (lua_getfield(L, lua_index, k->name)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) + { + entity_enable_tag_(iter->world, mainkey, idx, k->id, L, world_index); + } + else + { + entity_disable_tag_(iter->world, mainkey, idx, k->id); + } + break; + default: + luaL_error(L, ".%s is a tag , should be a boolean or nil. It's %s", k->name, lua_typename(L, lua_type(L, -1))); + } + lua_pop(L, 1); } - lua_insert(L, -2); - lua_rawseti(L, -2, index); } - else + else if ((k->attrib & COMPONENT_OUT) && get_write_component(L, lua_index, k->name, f, c)) + { + int index = entity_sibling_index_(iter->world, mainkey, idx, k->id); + if (index == 0) + { + luaL_error(L, "Can't find sibling %s of %s", k->name, iter->k[0].name); + } + if (c->stride == STRIDE_LUA) + { + if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) + { + luaL_error(L, "Missing lua table for %d", k->id); + } + lua_insert(L, -2); + lua_rawseti(L, -2, index); + } + else + { + void *buffer = get_ptr(c, index - 1); + write_component_object(L, k->field_n, f, buffer); + } + } + else if (is_temporary(k->attrib) && get_write_component(L, lua_index, k->name, f, c)) { - void *buffer = entity_add_sibling_(iter->world, mainkey, idx, k->id, NULL, L); - write_component_in_field(L, lua_index, k->name, k->field_n, f, buffer); + if (c->stride == STRIDE_LUA) + { + int index = entity_add_sibling_index_(L, world_index, iter->world, mainkey, idx, k->id); + if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) + { + luaL_error(L, "Missing lua table for %d", k->id); + } + lua_insert(L, -2); + lua_rawseti(L, -2, index); + } + else + { + void *buffer = entity_add_sibling_(iter->world, mainkey, idx, k->id, NULL, L, world_index); + write_component_object(L, k->field_n, f, buffer); + } } } f += k->field_n; @@ -1025,67 +1235,44 @@ read_component_in_field(lua_State *L, int lua_index, const char *name, int n, st lua_pop(L, 1); } +// -1 : end ; 0 : next ; 1 : succ static int -leach_group(lua_State *L) +query_index(struct group_iter *iter, int mainkey, int idx, unsigned int index[MAX_COMPONENT]) { - struct group_iter *iter = lua_touserdata(L, 1); - if (lua_rawgeti(L, 2, 1) != LUA_TNUMBER) - { - return luaL_error(L, "Invalid group iterator"); - } - int i = lua_tointeger(L, -1); - if (i < 0) - return luaL_error(L, "Invalid iterator index %d", i); - lua_pop(L, 1); - - if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) - { - return luaL_error(L, "Missing world object for iterator"); - } - int world_index = lua_gettop(L); - - if (i > 0 && !iter->readonly) + if (entity_iter_(iter->world, mainkey, idx) == NULL) { - update_last_index(L, world_index, 2, iter, i - 1); + return -1; } - int mainkey = iter->k[0].id; - unsigned int index[MAX_COMPONENT]; + index[0] = idx + 1; int j; - do + for (j = 1; j < iter->nkey; j++) { - if (iter->world->c[mainkey].count <= i) + struct group_key *k = &iter->k[j]; + if (!is_temporary(k->attrib)) { - return 0; - } - index[0] = i + 1; - for (j = 1; j < iter->nkey; j++) - { - struct group_key *k = &iter->k[j]; - if (!is_temporary(k->attrib)) + index[j] = entity_sibling_index_(iter->world, mainkey, idx, k->id); + if (index[j] == 0) { - index[j] = entity_sibling_index_(iter->world, mainkey, i, k->id); - if (index[j] == 0) + if (!(k->attrib & COMPONENT_OPTIONAL)) { - if (!(k->attrib & COMPONENT_OPTIONAL)) - { - // required. try next - break; - } + // required. try next + return 0; } } - else - { - index[j] = 0; - } } - ++i; - } while (j < iter->nkey); - - lua_pushinteger(L, i); - lua_rawseti(L, 2, 1); + else + { + index[j] = 0; + } + } + return 1; +} +static void +read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter, unsigned int index[MAX_COMPONENT]) +{ struct field *f = iter->f; - + int i; for (i = 0; i < iter->nkey; i++) { struct group_key *k = &iter->k[i]; @@ -1097,39 +1284,115 @@ leach_group(lua_State *L) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { - return luaL_error(L, "Missing lua table for %d", k->id); + luaL_error(L, "Missing lua table for %d", k->id); } lua_rawgeti(L, -1, index[i]); - lua_setfield(L, 2, k->name); + lua_setfield(L, obj_index, k->name); lua_pop(L, 1); } else { lua_pushnil(L); - lua_setfield(L, 2, k->name); + lua_setfield(L, obj_index, k->name); } } - else if (k->attrib & COMPONENT_IN) + else if (c->stride != STRIDE_ORDER) { - if (index[i]) - { - void *ptr = get_ptr(c, index[i] - 1); - read_component_in_field(L, 2, k->name, k->field_n, f, ptr); - } - else + if (!(k->attrib & COMPONENT_EXIST)) { - lua_pushnil(L); - lua_setfield(L, 2, k->name); + if (k->attrib & COMPONENT_IN) + { + if (index[i]) + { + void *ptr = get_ptr(c, index[i] - 1); + read_component_in_field(L, obj_index, k->name, k->field_n, f, ptr); + } + else + { + lua_pushnil(L); + lua_setfield(L, obj_index, k->name); + } + } + else if (index[i] == 0 && !is_temporary(k->attrib)) + { + lua_pushnil(L); + lua_setfield(L, obj_index, k->name); + } } + f += k->field_n; } - else if (index[i] == 0 && !is_temporary(k->attrib)) - { - lua_pushnil(L); - lua_setfield(L, 2, k->name); - } - f += k->field_n; } +} + +static int +lsync(lua_State *L) +{ + struct group_iter *iter = luaL_checkudata(L, 2, "ENTITY_GROUPITER"); + luaL_checktype(L, 3, LUA_TTABLE); + if (lua_rawgeti(L, 3, 1) != LUA_TNUMBER) + { + return luaL_error(L, "Invalid iterator"); + } + int idx = lua_tointeger(L, -1) - 1; + if (idx < 0) + return luaL_error(L, "Invalid iterator index %d", idx); + lua_pop(L, 1); + + if (!iter->readonly) + { + update_last_index(L, 1, 3, iter, idx); + } + + unsigned int index[MAX_COMPONENT]; + if (query_index(iter, iter->k[0].id, idx, index) <= 0) + { + return luaL_error(L, "Read pattern fail"); + } + read_iter(L, 1, 3, iter, index); + return 0; +} + +static int +leach_group(lua_State *L) +{ + struct group_iter *iter = lua_touserdata(L, 1); + if (lua_rawgeti(L, 2, 1) != LUA_TNUMBER) + { + return luaL_error(L, "Invalid group iterator"); + } + int i = lua_tointeger(L, -1); + if (i < 0) + return luaL_error(L, "Invalid iterator index %d", i); + lua_pop(L, 1); + + if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) + { + return luaL_error(L, "Missing world object for iterator"); + } + + int world_index = lua_gettop(L); + + if (i > 0 && !iter->readonly) + { + update_last_index(L, world_index, 2, iter, i - 1); + } + int mainkey = iter->k[0].id; + unsigned int index[MAX_COMPONENT]; + for (;;) + { + int ret = query_index(iter, mainkey, i++, index); + if (ret < 0) + return 0; + if (ret > 0) + break; + } + + lua_pushinteger(L, i); + lua_rawseti(L, 2, 1); + + read_iter(L, world_index, 2, iter, index); + lua_settop(L, 2); return 1; } @@ -1137,8 +1400,9 @@ leach_group(lua_State *L) static void create_key_cache(lua_State *L, struct group_key *k, struct field *f) { - if (k->field_n == 0) - { // is tag or object? + if (k->field_n == 0 // is tag or object? + || (k->attrib & COMPONENT_EXIST)) + { // only existence return; } if (k->field_n == 1 && f[0].key == NULL) @@ -1173,7 +1437,7 @@ lpairs_group(lua_State *L) struct group_iter *iter = lua_touserdata(L, 1); lua_pushcfunction(L, leach_group); lua_pushvalue(L, 1); - lua_createtable(L, 1, iter->nkey); + lua_createtable(L, 2, iter->nkey); int i; int opt = 0; struct field *f = iter->f; @@ -1202,6 +1466,8 @@ lpairs_group(lua_State *L) } lua_pushinteger(L, 0); lua_rawseti(L, -2, 1); + lua_pushinteger(L, iter->k[0].id); // mainkey + lua_rawseti(L, -2, 2); return 3; } @@ -1250,7 +1516,7 @@ get_key(struct entity_world *w, lua_State *L, struct group_key *key, struct fiel } key->id = lua_tointeger(L, -1); lua_pop(L, 1); - if (key->id < 0 || key->id >= MAX_COMPONENT || key->id == ENTITY_REMOVED || w->c[key->id].cap == 0) + if (key->id < 0 || key->id >= MAX_COMPONENT || w->c[key->id].cap == 0) { return luaL_error(L, "Invalid id %d", key->id); } @@ -1273,6 +1539,10 @@ get_key(struct entity_world *w, lua_State *L, struct group_key *key, struct fiel { attrib |= COMPONENT_OPTIONAL; } + if (check_boolean(L, "exist")) + { + attrib |= COMPONENT_EXIST; + } key->attrib = attrib; if (is_value(L, f)) { @@ -1312,7 +1582,7 @@ lgroupiter(lua_State *L) } if (nkey > MAX_COMPONENT) { - return luaL_error(L, "Too mant keys"); + return luaL_error(L, "Too many keys"); } for (i = 0; i < nkey; i++) { @@ -1350,16 +1620,31 @@ lgroupiter(lua_State *L) return luaL_error(L, ".%s is object component, no fields needed", iter->k[i].name); iter->k[i].attrib |= COMPONENT_OBJECT; } + else if (c->stride == STRIDE_ORDER) + { + if (i != 0) + { + return luaL_error(L, ".%s is an order key, must be main key", iter->k[i].name); + } + else if (iter->k[0].attrib & COMPONENT_OUT) + { + return luaL_error(L, ".%s is an order key, it should be readonly", iter->k[0].name); + } + } int attrib = iter->k[i].attrib; - int readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); + int readonly; + if (attrib & COMPONENT_EXIST) + readonly = 0; + else + readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); if (!readonly) iter->readonly = 0; } if (iter->k[0].attrib & COMPONENT_OPTIONAL) { - return luaL_error(L, "The first key should not be optional"); + return luaL_error(L, "The main key should not be optional"); } - if (!(iter->k[0].attrib & COMPONENT_IN) && !(iter->k[0].attrib & COMPONENT_OUT)) + if (is_temporary(iter->k[0].attrib)) { return luaL_error(L, "The main key can't be temporary"); } @@ -1372,12 +1657,473 @@ lgroupiter(lua_State *L) return 1; } +static int +get_integer(lua_State *L, int index, int i, const char *key) +{ + if (lua_rawgeti(L, index, i) != LUA_TNUMBER) + { + return luaL_error(L, "Can't find %s in iterator", key); + } + int r = lua_tointeger(L, -1); + lua_pop(L, 1); + if (r <= 0) + return luaL_error(L, "Invalid %s (%d)", key, r); + return r; +} + +static int +lremove(lua_State *L) +{ + struct entity_world *w = getW(L); + luaL_checktype(L, 2, LUA_TTABLE); + int iter = get_integer(L, 2, 1, "index") - 1; + int mainkey = get_integer(L, 2, 2, "mainkey"); + entity_remove_(w, mainkey, iter, L, 1); + return 0; +} + +static int +lsortkey(lua_State *L) +{ + struct entity_world *w = getW(L); + int oid = luaL_checkinteger(L, 2); + int cid = luaL_checkinteger(L, 3); + entity_sort_key_(w, oid, cid, L, 1); + return 0; +} + +static int +lsingleton(lua_State *L) +{ + struct group_iter *iter = luaL_checkudata(L, 1, "ENTITY_GROUPITER"); + int cid = iter->k[0].id; + struct entity_world *w = iter->world; + if (cid < 0 || cid >= MAX_COMPONENT) + { + return luaL_error(L, "Invalid singleton %d", cid); + } + struct component_pool *c = &w->c[cid]; + if (c->n == 0) + { + return luaL_error(L, "No singleton %d", cid); + } + if (c->stride == STRIDE_LUA) + { + // lua singleton + lua_settop(L, 2); + if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) + { + return luaL_error(L, "No world"); + } + if (lua_getiuservalue(L, -1, cid * 2 + 2) != LUA_TTABLE) + { + return luaL_error(L, "Missing lua table for %d", cid); + } + if (lua_isnil(L, 2)) + { + lua_rawgeti(L, -1, 1); + } + else + { + lua_pushvalue(L, 2); + lua_rawseti(L, -2, 1); + lua_settop(L, 2); + } + return 1; + } + else if (c->stride <= 0) + { + return luaL_error(L, "Invalid singleton %d", cid); + } + struct field *f = iter->f; + void *buffer = get_ptr(c, 0); + if (lua_isnoneornil(L, 2)) + { + // read singleton + lua_createtable(L, 0, iter->k[0].field_n); + if (f->key == NULL) + { + // value type + read_value(L, f, buffer); + } + else + { + int index = lua_gettop(L); + read_component(L, iter->k[0].field_n, f, index, buffer); + } + } + else + { + // write singleton + lua_pushvalue(L, 2); + write_component_object(L, iter->k[0].field_n, f, buffer); + } + return 1; +} + +// reference object + +struct ecs_ref +{ + struct ecs_ref_i interface; + void *data; + lua_State *L; + int n; + int cap; + int stride; + int nkey; + int freelist; + struct field f[1]; +}; + +struct ref_freenode +{ + int freelist; +}; + +static void +init_fields(struct ecs_ref *R, lua_State *L, int index, int nkey) +{ + if (nkey == 0) + { + // lua object + R->stride = 0; + lua_newtable(R->L); + if (lua_gettop(R->L) != 1) + { + luaL_error(L, "Invalid ref context"); + } + } + else + { + if (lua_getfield(L, index, "size") != LUA_TNUMBER) + { + luaL_error(L, "need .size"); + } + R->stride = lua_tointeger(L, -1); + lua_pop(L, 1); + int i; + for (i = 0; i < nkey; i++) + { + struct field *f = &R->f[i]; + if (lua_geti(L, index, i + 1) != LUA_TTABLE) + { + luaL_error(L, "Invalid field %d, need table, it's %s", i + 1, lua_typename(L, lua_type(L, -1))); + } + if (lua_geti(L, -1, 1) != LUA_TNUMBER) + { + luaL_error(L, "Invalid type id in [%d]", i + 1); + } + f->type = lua_tointeger(L, -1); + lua_pop(L, 1); + if (lua_geti(L, -1, 2) != LUA_TSTRING) + { + if (lua_type(L, -1) == LUA_TNIL && i == 0) + { + f->key = NULL; + } + else + { + luaL_error(L, "Invalid key name in [%d]", i + 1); + } + } + else + { + f->key = lua_tostring(L, -1); + } + lua_pop(L, 1); + if (lua_geti(L, -1, 3) != LUA_TNUMBER) + { + luaL_error(L, "Invalid offset in [%d]", i + 1); + } + f->offset = lua_tointeger(L, -1); + if (f->offset >= R->stride) + { + luaL_error(L, "Invalid offset (%d/%d) in [%d]", f->offset, R->stride, i + 1); + } + lua_pop(L, 2); + } + } +} + +static inline void * +ref_index_ptr(struct ecs_ref *R, int index) +{ + return (void *)((char *)R->data + R->stride * index); +} + +static int +alloc_id(struct ecs_ref *R) +{ + if (R->data == NULL) + { + lua_settop(R->L, 0); + R->data = lua_newuserdata(R->L, R->stride * R->cap); + R->n = 1; + return 1; + } + if (R->freelist) + { + int r = R->freelist - 1; + struct ref_freenode *node = (struct ref_freenode *)ref_index_ptr(R, r); + R->freelist = node->freelist; + return r; + } + if (R->n >= R->cap) + { + int newcap = R->cap * 3 / 2; + void *data = lua_newuserdata(R->L, R->stride * newcap); + memcpy(R->data, data, R->stride * R->n); + lua_replace(R->L, 1); + R->data = data; + R->cap = newcap; + } + return ++R->n; +} + +static int +lnew_object(lua_State *L) +{ + struct ecs_ref *R = (struct ecs_ref *)luaL_checkudata(L, 1, "ENTITY_REFOBJECT"); + int n; + if (R->stride == 0) + { + // lua object + if (!lua_istable(R->L, 1)) + { + return luaL_error(L, "Invalid ref context"); + } + n = lua_rawlen(R->L, 1); + lua_settop(L, 2); + lua_xmove(L, R->L, 1); + lua_rawseti(R->L, 1, n + 1); + lua_pushinteger(L, n + 1); + } + else + { + n = alloc_id(R); + void *buffer = ref_index_ptr(R, n - 1); + if (R->f[0].key == NULL) + { + write_value(L, R->f, buffer); + } + else + { + write_component(L, R->nkey, R->f, 2, buffer); + } + lua_pushinteger(L, n); + } + return 1; +} + +static void +release_object(struct ecs_ref *R, int id) +{ + if (R->stride == 0) + { + // lua object + if (!lua_istable(R->L, 1)) + { + luaL_error(R->L, "Invalid ref context"); + } + lua_pushnil(R->L); + lua_rawseti(R->L, 1, id); + } + else + { + if (id <= 0 || id > R->n) + luaL_error(R->L, "Invalid id %d", id); + if (id == R->n) + { + --R->n; + } + else + { + struct ref_freenode *node = (struct ref_freenode *)ref_index_ptr(R, id - 1); + node->freelist = R->freelist; + R->freelist = id; + } + } +} + +static void * +index_object(struct ecs_ref *R, int id) +{ + if (R->stride == 0) + { + luaL_error(R->L, "Can't index lua object"); + } + else if (id <= 0 || id > R->n) + { + luaL_error(R->L, "Invalid id %d", id); + } + return ref_index_ptr(R, id - 1); +} + +static int +ldelete_object(lua_State *L) +{ + struct ecs_ref *R = (struct ecs_ref *)luaL_checkudata(L, 1, "ENTITY_REFOBJECT"); + int id = luaL_checkinteger(L, 2); + release_object(R, id); + return 0; +} + +#define KEY(s) s, sizeof(""s) + +static int +iskey(const char *name, size_t sz, const char *keyname, size_t keysize) +{ + return (sz + 1 == keysize) && memcmp(name, keyname, sz) == 0; +} + +static inline int +method(lua_State *L, int index) +{ + size_t sz; + const char *name = lua_tolstring(L, index, &sz); + if (iskey(name, sz, KEY("new"))) + { + lua_pushcfunction(L, lnew_object); + } + else if (iskey(name, sz, KEY("delete"))) + { + lua_pushcfunction(L, ldelete_object); + } + else + { + return luaL_error(L, "Invalid method %s", name); + } + return 1; +} + +static inline int +index_ref(lua_State *L, int index) +{ + int id = lua_tointeger(L, index); + if (id <= 0) + { + return luaL_error(L, "Invalid index %d", id); + } + struct ecs_ref *R = (struct ecs_ref *)lua_touserdata(L, 1); + if (R->stride == 0) + { + // lua object + if (!lua_istable(R->L, 1)) + { + return luaL_error(L, "Invalid ref context"); + } + lua_rawgeti(R->L, 1, id); + lua_xmove(R->L, L, 1); + } + else + { + if (id <= 0 || id > R->n) + { + return luaL_error(L, "Invalid id %d\n", id); + } + void *buffer = ref_index_ptr(R, id - 1); + if (R->f->key == NULL) + { + read_value(L, R->f, buffer); + } + else + { + lua_createtable(L, 0, R->nkey); + int index = lua_gettop(L); + read_component(L, R->nkey, R->f, index, buffer); + } + } + return 1; +} + +static int +lread_ref(lua_State *L) +{ + switch (lua_type(L, 2)) + { + case LUA_TSTRING: + return method(L, 2); + case LUA_TNUMBER: + return index_ref(L, 2); + default: + return luaL_error(L, "Invalid key type %s", lua_typename(L, lua_type(L, -1))); + } +} + +static int +lwrite_ref(lua_State *L) +{ + struct ecs_ref *R = (struct ecs_ref *)lua_touserdata(L, 1); + int id = luaL_checkinteger(L, 2); + if (R->stride == 0) + { + // lua object + if (!lua_istable(R->L, 1)) + { + return luaL_error(L, "Invalid ref context"); + } + lua_xmove(L, R->L, 1); + lua_rawseti(R->L, 1, id); + } + if (id <= 0 && id > R->n) + return luaL_error(L, "Invalid id %d", id); + void *buffer = ref_index_ptr(R, id - 1); + write_component_object(L, R->nkey, R->f, buffer); + return 0; +} + +static int +lnew_ref(lua_State *L) +{ + size_t sz; + int nkey; + if (lua_isnoneornil(L, 1)) + { + sz = offsetof(struct ecs_ref, f); + nkey = 0; + } + else + { + luaL_checktype(L, 1, LUA_TTABLE); + nkey = get_len(L, 1); + sz = sizeof(struct ecs_ref) + (nkey - 1) * sizeof(struct field); + } + struct ecs_ref *R = (struct ecs_ref *)lua_newuserdatauv(L, sz, 1); + static struct ecs_capi_ref apis = { + alloc_id, + release_object, + index_object, + }; + R->interface.api = &apis; + R->data = NULL; + R->n = 0; + R->cap = DEFAULT_SIZE; + R->nkey = nkey; + R->freelist = 0; + R->L = lua_newthread(L); + lua_setiuservalue(L, -2, 1); + init_fields(R, L, 1, nkey); + if (luaL_newmetatable(L, "ENTITY_REFOBJECT")) + { + luaL_Reg l[] = { + {"__index", lread_ref}, + {"__newindex", lwrite_ref}, + {NULL, NULL}, + }; + luaL_setfuncs(L, l, 0); + } + lua_setmetatable(L, -2); + return 1; +} + LUAMOD_API int luaopen_ecs_core(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { - {"world", lnew_world}, + {"_world", lnew_world}, + {"_ref", lnew_ref}, {"_MAXTYPE", NULL}, {"_METHODS", NULL}, {"_TYPEINT", NULL}, @@ -1397,12 +2143,14 @@ luaopen_ecs_core(lua_State *L) {"_newtype", lnew_type}, {"_newentity", lnew_entity}, {"_addcomponent", ladd_component}, - {"remove", lremove_entity}, - {"sort_removed", lsort_removed}, {"update", lupdate}, - {"clear", lclear_type}, + {"_clear", lclear_type}, {"_context", lcontext}, {"_groupiter", lgroupiter}, + {"remove", lremove}, + {"_sortkey", lsortkey}, + {"_singleton", lsingleton}, + {"_sync", lsync}, {NULL, NULL}, }; luaL_setfuncs(L, l, 0); @@ -1422,6 +2170,10 @@ luaopen_ecs_core(lua_State *L) lua_setfield(L, -2, "_TYPEBOOL"); lua_pushinteger(L, STRIDE_LUA); lua_setfield(L, -2, "_LUAOBJECT"); + lua_pushinteger(L, ENTITY_REMOVED); + lua_setfield(L, -2, "_REMOVED"); + lua_pushinteger(L, STRIDE_ORDER); + lua_setfield(L, -2, "_ORDERKEY"); return 1; } @@ -1433,6 +2185,7 @@ luaopen_ecs_core(lua_State *L) #define COMPONENT_VECTOR2 1 #define TAG_MARK 2 #define COMPONENT_ID 3 +#define SINGLETON_STRING 4 struct vector2 { @@ -1484,6 +2237,31 @@ lsum(lua_State *L) return 1; } +static int +lget(lua_State *L) +{ + struct ecs_context *ctx = lua_touserdata(L, 1); + const char *ret = (const char *)entity_iter_lua(ctx, SINGLETON_STRING, 0); + if (ret) + { + lua_pushfstring(L, "[string:%s]", ret); + } + return 1; +} + +static int +ltestref(lua_State *L) +{ + struct ecs_ref_i *R = (struct ecs_ref_i *)lua_touserdata(L, 1); + int id = robject_create(R); + printf("create id = %d\n", id); + struct vector2 *vec = robject_index(R, id); + vec->x = 42.0f; + vec->y = 24.0f; + lua_pushinteger(L, id); + return 1; +} + LUAMOD_API int luaopen_ecs_ctest(lua_State *L) { @@ -1491,6 +2269,8 @@ luaopen_ecs_ctest(lua_State *L) luaL_Reg l[] = { {"test", ltest}, {"sum", lsum}, + {"get", lget}, + {"testref", ltestref}, {NULL, NULL}, }; luaL_newlib(L, l); diff --git a/framework/lualib-src/lua-ecs/luaecs.h b/framework/lualib-src/lua-ecs/luaecs.h index 55f9353..d0d01c4 100644 --- a/framework/lualib-src/lua-ecs/luaecs.h +++ b/framework/lualib-src/lua-ecs/luaecs.h @@ -10,8 +10,12 @@ struct ecs_capi void *(*iter)(struct entity_world *w, int cid, int index); void (*clear_type)(struct entity_world *w, int cid); void *(*sibling)(struct entity_world *w, int cid, int index, int slibling_id); - void *(*add_sibling)(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L); - void (*remove)(struct entity_world *w, int cid, int index, void *L); + void *(*add_sibling)(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L, int world_index); + void (*remove)(struct entity_world *w, int cid, int index, void *L, int world_index); + void (*enable_tag)(struct entity_world *w, int cid, int index, int tag_id, void *L, int world_index); + void (*disable_tag)(struct entity_world *w, int cid, int index, int tag_id); + void (*sort_key)(struct entity_world *w, int orderid, int cid, void *L, int world_index); + void *(*iter_lua)(struct entity_world *w, int cid, int index, void *L); }; struct ecs_context @@ -56,14 +60,77 @@ entity_add_sibling(struct ecs_context *ctx, int cid, int index, int slibling_id, { check_id_(ctx, cid); check_id_(ctx, slibling_id); - return ctx->api->add_sibling(ctx->world, ctx->cid[cid], index, ctx->cid[slibling_id], buffer, ctx->L); + return ctx->api->add_sibling(ctx->world, ctx->cid[cid], index, ctx->cid[slibling_id], buffer, ctx->L, 1); } static inline void entity_remove(struct ecs_context *ctx, int cid, int index) { check_id_(ctx, cid); - ctx->api->remove(ctx->world, ctx->cid[cid], index, ctx->L); + ctx->api->remove(ctx->world, ctx->cid[cid], index, ctx->L, 1); +} + +static inline void +entity_enable_tag(struct ecs_context *ctx, int cid, int index, int tag_id) +{ + check_id_(ctx, cid); + check_id_(ctx, tag_id); + ctx->api->enable_tag(ctx->world, ctx->cid[cid], index, ctx->cid[tag_id], ctx->L, 1); +} + +static inline void +entity_disable_tag(struct ecs_context *ctx, int cid, int index, int tag_id) +{ + check_id_(ctx, cid); + check_id_(ctx, tag_id); + ctx->api->disable_tag(ctx->world, ctx->cid[cid], index, ctx->cid[tag_id]); +} + +static inline void +entity_sort_key(struct ecs_context *ctx, int orderid, int cid) +{ + check_id_(ctx, orderid); + check_id_(ctx, cid); + ctx->api->sort_key(ctx->world, ctx->cid[orderid], ctx->cid[cid], ctx->L, 1); +} + +static inline void * +entity_iter_lua(struct ecs_context *ctx, int cid, int index) +{ + check_id_(ctx, cid); + return ctx->api->iter_lua(ctx->world, ctx->cid[cid], index, ctx->L); +} + +struct ecs_ref_i +{ + struct ecs_capi_ref *api; +}; + +struct ecs_ref; + +struct ecs_capi_ref +{ + int (*create)(struct ecs_ref *); + void (*release)(struct ecs_ref *, int id); + void *(*index)(struct ecs_ref *, int id); +}; + +static inline int +robject_create(struct ecs_ref_i *R) +{ + return R->api->create((struct ecs_ref *)R); +} + +static inline void +robject_release(struct ecs_ref_i *R, int id) +{ + R->api->release((struct ecs_ref *)R, id); +} + +static inline void * +robject_index(struct ecs_ref_i *R, int id) +{ + return R->api->index((struct ecs_ref *)R, id); } #endif diff --git a/framework/lualib-src/lua-ecs/test.lua b/framework/lualib-src/lua-ecs/test.lua deleted file mode 100644 index b1ffd4e..0000000 --- a/framework/lualib-src/lua-ecs/test.lua +++ /dev/null @@ -1,165 +0,0 @@ -local ecs = require "ecs" - -local N = 1 - -local w = ecs.world() -print("memory:", w:memory()) - -w:register{ - name = "vector", - "x:float", - "y:float", -} - -w:register{ - name = "mark", -} - -w:register{ - name = "id", - type = "int", -} - -w:register{ - name = "object", - type = "lua", -} - -w:new{ - object = "Hello", -} - -local t = {} -for i = 1, N do - w:new{ - vector = { - x = 1, - y = 2, - }, - } - t[i] = { - x = 1, - y = 2, - } -end - -w:update() - -local function swap_c() - for v in w:select "vector:update" do - local vec = v.vector - vec.x, vec.y = vec.y, vec.x - end -end - -local function swap_lua() - for _, v in ipairs(t) do - v.x, v.y = v.y, v.x - end -end - -local function timing(f) - local c = os.clock() - for i = 1, 100 do - f() - end - return os.clock() - c -end - -print("memory:", w:memory()) - -print("CSWAP", timing(swap_c)) -print("LUASWAP", timing(swap_lua)) - -w:new{ - vector = { - x = 3, - y = 4, - }, - id = 100, -} - -table.insert(t, { - x = 3, - y = 4, -}) - -w:new{ - vector = { - x = 5, - y = 6, - }, - mark = true, -} - -table.insert(t, { - x = 5, - y = 6, -}) - -w:update() - -local context = w:context{"vector", "mark", "id"} -local test = require "ecs.ctest" -local function csum() - return test.sum(context) -end -print("csum = ", csum()) - -local function luasum() - local s = 0 - for v in w:select "vector:in" do - s = s + v.vector.x + v.vector.y - end - return s -end - -print("luasum = ", luasum()) - -local function luanativesum() - local s = 0 - for _, v in ipairs(t) do - s = s + v.x + v.y - end - return s -end - -print("lnative sum = ", luanativesum()) - -print("CSUM", timing(csum)) -print("LUASUM", timing(luasum)) -print("LNATIVESUM", timing(luanativesum)) - -print "vector:update" -for v in w:select "vector:update" do - local vec = v.vector - print(vec.x, vec.y) - vec.x, vec.y = vec.y, vec.x -end - -print "vector:in id?out" -for v in w:select "vector:in id?out" do - print(v.vector.x, v.vector.y, v.id) - if v.id then - v.id = 200 - end -end - -print "vector:in id:in" - -for v in w:select "vector:in id:in" do - print(v.vector.x, v.vector.y, v.id) -end - -print "object:update" - -for v in w:select "object:update" do - print(v.object) - v.object = v.object .. " world" -end - -print "object:in" - -for v in w:select "object:in" do - print(v.object) -end diff --git a/framework/lualib-src/lua-math3d/README.md b/framework/lualib-src/lua-math3d/README.md new file mode 100644 index 0000000..39aeb04 --- /dev/null +++ b/framework/lualib-src/lua-math3d/README.md @@ -0,0 +1,5 @@ +## 用途 + +现在3d游戏开发已经很普遍,如果服务端涉及到3d数学方面的计算就需要一个开源的3d数学库,网上开源的3d数学库大部分都是右手坐标。而我们所使用的客户端大部分都是unity,众所周知unity是左手坐标系的数学库。所以一个基于左手坐标系的3d数学库就很有必要。本库是基于云风的项目[ejoy3d](https://github.com/cloudwu/ejoy3d)所修改。包含左手库和右手库,如要切换可直接在libmath.c的首行进行修改。 + +本项目是基于lua的动态库,如果不需要使用lua,可直接使用math3d-left.h和math3d-right.h diff --git a/framework/lualib-src/lua-math3d/lua-math3d.c b/framework/lualib-src/lua-math3d/lua-math3d.c new file mode 100644 index 0000000..8d3a792 --- /dev/null +++ b/framework/lualib-src/lua-math3d/lua-math3d.c @@ -0,0 +1,853 @@ +#include "math3d-left.h" +#include +#include + +// https://github.com/huanzai/math3d + +static void * +check_userdata(lua_State *L, int idx) +{ + void *ret = lua_touserdata(L, idx); + luaL_argcheck(L, ret != NULL, idx, "Userdata should not be NULL"); + return ret; +} + +static int +lnewvec3(lua_State *L) +{ + struct vector3 tmp; + if (lua_isuserdata(L, 1)) + { + struct vector3 *copy = check_userdata(L, 1); + tmp = *copy; + } + else + { + tmp.x = luaL_optnumber(L, 1, 0); + tmp.y = luaL_optnumber(L, 2, 0); + tmp.z = luaL_optnumber(L, 3, 0); + } + struct vector3 *vec3 = lua_newuserdata(L, sizeof(*vec3)); + *vec3 = tmp; + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +static int +lvec3_pack(lua_State *L) +{ + struct vector3 *vec3 = check_userdata(L, 1); + vec3->x = luaL_optnumber(L, 2, 0); + vec3->y = luaL_optnumber(L, 3, 0); + vec3->z = luaL_optnumber(L, 4, 0); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_unpack(lua_State *L) +{ + struct vector3 *vec3 = check_userdata(L, 1); + lua_pushnumber(L, vec3->x); + lua_pushnumber(L, vec3->y); + lua_pushnumber(L, vec3->z); + return 3; +} + +static int +lvec3_dot(lua_State *L) +{ + struct vector3 *a = check_userdata(L, 1); + struct vector3 *b = check_userdata(L, 2); + float v = vector3_dot(a, b); + lua_pushnumber(L, v); + return 1; +} + +static int +lvec3_cross(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *a = check_userdata(L, 2); + struct vector3 *b = check_userdata(L, 3); + vector3_cross(v, a, b); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_vector(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *a = check_userdata(L, 2); + struct vector3 *b = check_userdata(L, 3); + vector3_vector(v, a, b); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_length(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + float len = vector3_length(v); + lua_pushnumber(L, len); + return 1; +} + +static int +lvec3_normalize(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + vector3_normalize(v); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_copy(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *from = check_userdata(L, 2); + *v = *from; + lua_settop(L, 1); + return 1; +} + +static int +lvec3_tostring(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + lua_pushfstring(L, "[%f, %f, %f]", v->x, v->y, v->z); + + return 1; +} + +static int +lvec3_rotation(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *from = check_userdata(L, 2); + vector3_to_rotation(v, from); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_lerp(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *a = check_userdata(L, 2); + struct vector3 *b = check_userdata(L, 3); + float f = luaL_checknumber(L, 4); + vector3_lerp(v, a, b, f); + lua_settop(L, 1); + return 1; +} + +static void +create_meta(lua_State *L, luaL_Reg *l, const char *name, lua_CFunction tostring) +{ + int n = 0; + while (l[n].name) + ++n; + lua_newtable(L); + lua_createtable(L, 0, n); + int i; + for (i = 0; i < n; i++) + { + lua_pushcfunction(L, l[i].func); + lua_setfield(L, -2, l[i].name); + } + lua_setfield(L, -2, "__index"); + lua_pushstring(L, name); + lua_setfield(L, -2, "__metatable"); + lua_pushcfunction(L, tostring); + lua_setfield(L, -2, "__tostring"); +} + +static int +lvec3_transmat(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + matrix44_trans(m, v->x, v->y, v->z); + lua_settop(L, 2); + return 1; +} + +static int +lvec3_scalemat(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + matrix44_scale(m, v->x, v->y, v->z); + lua_settop(L, 2); + return 1; +} + +static int +lvec3_rotmat(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + matrix44_rot(m, v->x, v->y, v->z); + lua_settop(L, 2); + return 1; +} + +static int +lvec3_rotaxis(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + float angle = luaL_checknumber(L, 3); + matrix44_rot_axis(m, v, angle); + lua_settop(L, 2); + return 1; +} + +static int +lvec3_mul(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + vector3_mul(v, m); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_mul33(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + vector3_mul33(v, m); + lua_settop(L, 1); + return 1; +} + +static int +lvec3_distAABB(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct vector3 *mins = check_userdata(L, 2); + struct vector3 *maxs = check_userdata(L, 3); + float d = vector3_distAABB(v, mins, maxs); + lua_pushnumber(L, d); + return 1; +} + +static int +lvec3_plane(lua_State *L) +{ + struct vector3 *v = check_userdata(L, 1); + struct plane *p = check_userdata(L, 2); + float d = luaL_optnumber(L, 3, 0); + plane_init(p, v, d); + lua_settop(L, 2); + return 1; +} + +static void +vector3(lua_State *L) +{ + luaL_Reg l[] = { + {"pack", lvec3_pack}, + {"unpack", lvec3_unpack}, + {"dot", lvec3_dot}, + {"cross", lvec3_cross}, + {"vector", lvec3_vector}, + {"length", lvec3_length}, + {"normalize", lvec3_normalize}, + {"copy", lvec3_copy}, + {"rotation", lvec3_rotation}, + {"lerp", lvec3_lerp}, + {"transmat", lvec3_transmat}, + {"scalemat", lvec3_scalemat}, + {"rotmat", lvec3_rotmat}, + {"rotaxis", lvec3_rotaxis}, + {"mul", lvec3_mul}, + {"mul33", lvec3_mul33}, + {"plane", lvec3_plane}, + {"distAABB", lvec3_distAABB}, + {NULL, NULL}, + }; + create_meta(L, l, "vector3", lvec3_tostring); + lua_pushcclosure(L, lnewvec3, 1); +} + +static int +lnewquat(lua_State *L) +{ + if (lua_isuserdata(L, 1)) + { + struct quaternion *tmp = check_userdata(L, 1); + struct quaternion *q = lua_newuserdata(L, sizeof(*q)); + *q = *tmp; + } + else if lua_isnoneornil (L, 1) + { + struct quaternion *q = lua_newuserdata(L, sizeof(*q)); + q->x = 0; + q->y = 0; + q->z = 0; + q->w = 1.0f; + } + else + { + float x = luaL_checknumber(L, 1); + float y = luaL_checknumber(L, 2); + float z = luaL_checknumber(L, 3); + struct quaternion *q = lua_newuserdata(L, sizeof(*q)); + quaternion_init(q, x, y, z); + } + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + + return 1; +} + +static int +lquat_tostring(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + lua_pushfstring(L, "[%f, %f, %f, %f]", q->x, q->y, q->z, q->w); + + return 1; +} + +static int +lquat_mul(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + struct quaternion *a = check_userdata(L, 2); + struct quaternion *b = check_userdata(L, 3); + quaternion_mul(q, a, b); + lua_settop(L, 1); + return 1; +} + +static int +lquat_copy(lua_State *L) +{ + struct quaternion *a = check_userdata(L, 1); + struct quaternion *b = check_userdata(L, 2); + *a = *b; + lua_settop(L, 1); + return 1; +} + +static int +lquat_slerp(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + struct quaternion *a = check_userdata(L, 2); + struct quaternion *b = check_userdata(L, 3); + float t = luaL_checknumber(L, 4); + quaternion_slerp(q, a, b, t); + lua_settop(L, 1); + return 1; +} + +static int +lquat_nslerp(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + struct quaternion *a = check_userdata(L, 2); + struct quaternion *b = check_userdata(L, 3); + float t = luaL_checknumber(L, 4); + quaternion_nslerp(q, a, b, t); + lua_settop(L, 1); + return 1; +} + +static int +lquat_inverted(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + quaternion_inverted(q); + lua_settop(L, 1); + return 1; +} + +static int +lquat_matrix(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + union matrix44 *mat = check_userdata(L, 2); + matrix44_from_quaternion(mat, q); + lua_settop(L, 2); + return 1; +} + +static int +lquat_pack(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + q->x = luaL_checknumber(L, 2); + q->y = luaL_checknumber(L, 3); + q->z = luaL_checknumber(L, 4); + q->w = luaL_checknumber(L, 5); + lua_settop(L, 1); + return 1; +} + +static int +lquat_unpack(lua_State *L) +{ + struct quaternion *q = check_userdata(L, 1); + lua_pushnumber(L, q->x); + lua_pushnumber(L, q->y); + lua_pushnumber(L, q->z); + lua_pushnumber(L, q->w); + return 4; +} + +static void +quaternion(lua_State *L) +{ + luaL_Reg l[] = { + {"mul", lquat_mul}, + {"copy", lquat_copy}, + {"pack", lquat_pack}, + {"unpack", lquat_unpack}, + {"slerp", lquat_slerp}, + {"nslerp", lquat_nslerp}, + {"inverted", lquat_inverted}, + {"matrix", lquat_matrix}, + {NULL, NULL}, + }; + create_meta(L, l, "quateraion", lquat_tostring); + lua_pushcclosure(L, lnewquat, 1); +} + +static int +lnewmat(lua_State *L) +{ + if (lua_isuserdata(L, 1)) + { + union matrix44 *tmp = check_userdata(L, 1); + union matrix44 *mat = lua_newuserdata(L, sizeof(*mat)); + *mat = *tmp; + } + else if lua_isnoneornil (L, 1) + { + union matrix44 *mat = lua_newuserdata(L, sizeof(*mat)); + matrix44_identity(mat); + } + else + { + float x = luaL_checknumber(L, 1); + float y = luaL_checknumber(L, 2); + float z = luaL_checknumber(L, 3); + union matrix44 *mat = lua_newuserdata(L, sizeof(*mat)); + matrix44_rot(mat, x, y, z); + } + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + + return 1; +} + +static int +lmat_tostring(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + lua_pushfstring(L, "[(%f, %f, %f, %f) (%f, %f, %f, %f) (%f, %f, %f, %f) (%f, %f, %f, %f)]", + m->c[0][0], m->c[0][1], m->c[0][2], m->c[0][3], + m->c[1][0], m->c[1][1], m->c[1][2], m->c[1][3], + m->c[2][0], m->c[2][1], m->c[2][2], m->c[2][3], + m->c[3][0], m->c[3][1], m->c[3][2], m->c[3][3]); + return 1; +} + +static int +lmat_pack(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + int i; + for (i = 0; i < 16; i++) + { + m->x[i] = luaL_checknumber(L, 2 + i); + } + lua_settop(L, 1); + return 1; +} + +static int +lmat_unpack(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + int i; + for (i = 0; i < 16; i++) + { + lua_pushnumber(L, m->x[i]); + } + return 16; +} + +static int +lmat_copy(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + union matrix44 *from = check_userdata(L, 2); + *m = *from; + lua_settop(L, 1); + return 1; +} + +static int +lmat_identity(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + matrix44_identity(m); + lua_settop(L, 1); + return 1; +} + +static int +lmat_perspective(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + float l = luaL_checknumber(L, 2); + float r = luaL_checknumber(L, 3); + float b = luaL_checknumber(L, 4); + float t = luaL_checknumber(L, 5); + float n = luaL_checknumber(L, 6); + float f = luaL_checknumber(L, 7); + matrix44_perspective(m, l, r, b, t, n, f); + + lua_settop(L, 1); + return 1; +} + +static int +lmat_ortho(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + float l = luaL_checknumber(L, 2); + float r = luaL_checknumber(L, 3); + float b = luaL_checknumber(L, 4); + float t = luaL_checknumber(L, 5); + float n = luaL_checknumber(L, 6); + float f = luaL_checknumber(L, 7); + matrix44_ortho(m, l, r, b, t, n, f); + + lua_settop(L, 1); + return 1; +} + +static int +lmat_mul(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + union matrix44 *a = check_userdata(L, 2); + union matrix44 *b = check_userdata(L, 3); + if (b == NULL) + { + b = a; + a = m; + } + matrix44_mul(m, a, b); + lua_settop(L, 1); + return 1; +} + +static int +lmat_fastmul43(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + union matrix44 *a = check_userdata(L, 2); + union matrix44 *b = check_userdata(L, 3); + matrix44_fastmul43(m, a, b); + lua_settop(L, 1); + return 1; +} + +static int +lmat_transposed(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + matrix44_transposed(m); + lua_settop(L, 1); + return 1; +} + +static int +lmat_determinant(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + float v = matrix44_determinant(m); + lua_pushnumber(L, v); + return 1; +} + +static int +lmat_inverted(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + union matrix44 *from = check_userdata(L, 2); + matrix44_inverted(m, from); + lua_settop(L, 1); + return 1; +} + +static int +lmat_trans(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + struct vector3 *v = check_userdata(L, 2); + matrix44_gettrans(m, v); + lua_settop(L, 2); + return 1; +} + +static int +lmat_scale(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + struct vector3 *v = check_userdata(L, 2); + matrix44_getscale(m, v); + lua_settop(L, 2); + return 1; +} + +static int +lmat_decompose(lua_State *L) +{ + union matrix44 *m = check_userdata(L, 1); + struct vector3 *trans = check_userdata(L, 2); + struct vector3 *rot = check_userdata(L, 3); + struct vector3 *scale = check_userdata(L, 4); + matrix44_decompose(m, trans, rot, scale); + lua_settop(L, 4); + return 3; +} + +static void +matrix(lua_State *L) +{ + luaL_Reg l[] = { + {"pack", lmat_pack}, + {"unpack", lmat_unpack}, + {"copy", lmat_copy}, + {"identity", lmat_identity}, + {"perspective", lmat_perspective}, + {"ortho", lmat_ortho}, + {"mul", lmat_mul}, + {"fastmul43", lmat_fastmul43}, + {"transposed", lmat_transposed}, + {"determinant", lmat_determinant}, + {"inverted", lmat_inverted}, + {"trans", lmat_trans}, + {"scale", lmat_scale}, + {"decompose", lmat_decompose}, + {NULL, NULL}, + }; + create_meta(L, l, "matrix", lmat_tostring); + lua_pushcclosure(L, lnewmat, 1); +} + +static int +lnewvec4(lua_State *L) +{ + struct vector4 tmp; + if (lua_isuserdata(L, 1)) + { + struct vector4 *copy = check_userdata(L, 1); + tmp = *copy; + } + else + { + tmp.x = luaL_optnumber(L, 1, 0); + tmp.y = luaL_optnumber(L, 2, 0); + tmp.z = luaL_optnumber(L, 3, 0); + tmp.z = luaL_optnumber(L, 4, 1.0); + } + struct vector4 *vec4 = lua_newuserdata(L, sizeof(*vec4)); + *vec4 = tmp; + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +#define lvec4_tostring lquat_tostring +#define lvec4_copy lquat_copy +#define lvec4_pack lquat_pack +#define lvec4_unpack lquat_unpack + +static int +lvec4_mul(lua_State *L) +{ + struct vector4 *v = check_userdata(L, 1); + union matrix44 *m = check_userdata(L, 2); + vector4_mul(v, m); + lua_settop(L, 1); + return 1; +} + +static void +vector4(lua_State *L) +{ + luaL_Reg l[] = { + {"copy", lvec4_copy}, + {"pack", lvec4_pack}, + {"unpack", lvec4_unpack}, + {"mul", lvec4_mul}, + {NULL, NULL}, + }; + create_meta(L, l, "vector4", lvec4_tostring); + lua_pushcclosure(L, lnewvec4, 1); +} + +static int +lnewplane(lua_State *L) +{ + int top = lua_gettop(L); + if (top == 0) + { + struct plane *p = lua_newuserdata(L, sizeof(*p)); + p->normal.x = 0; + p->normal.y = 0; + p->normal.z = 1; + p->dist = 0; // XY plane + } + else if (top == 1) + { + struct plane *copy = check_userdata(L, 1); + struct plane *p = lua_newuserdata(L, sizeof(*p)); + *p = *copy; + } + else if (top == 3) + { + struct vector3 *a = check_userdata(L, 1); + struct vector3 *b = check_userdata(L, 2); + struct vector3 *c = check_userdata(L, 3); + struct plane *p = lua_newuserdata(L, sizeof(*p)); + plane_init_dot3(p, a, b, c); + } + else + { + return luaL_error(L, "Invalid new plane"); + } + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +static int +lplane_tostring(lua_State *L) +{ + struct plane *p = check_userdata(L, 1); + lua_pushfstring(L, "[%f, %f, %f : %f]", p->normal.x, p->normal.x, p->normal.z, p->dist); + return 1; +} + +static int +lplane_dist(lua_State *L) +{ + struct plane *p = check_userdata(L, 1); + struct vector3 *v = check_userdata(L, 2); + float d = plane_dist(p, v); + lua_pushnumber(L, d); + return 1; +} + +static int +lplane_copy(lua_State *L) +{ + struct plane *p = check_userdata(L, 1); + struct plane *from = check_userdata(L, 2); + *p = *from; + lua_settop(L, 1); + return 1; +} + +static int +lplane_dot3(lua_State *L) +{ + struct plane *p = check_userdata(L, 1); + struct vector3 *a = check_userdata(L, 2); + struct vector3 *b = check_userdata(L, 3); + struct vector3 *c = check_userdata(L, 4); + plane_init_dot3(p, a, b, c); + lua_settop(L, 1); + return 1; +} + +static void +plane(lua_State *L) +{ + luaL_Reg l[] = { + {"copy", lplane_copy}, + {"dist", lplane_dist}, + {"dot3", lplane_dot3}, + {NULL, NULL}, + }; + create_meta(L, l, "plane", lplane_tostring); + lua_pushcclosure(L, lnewplane, 1); +} + +static int +lraytriangle(lua_State *L) +{ + int top = lua_gettop(L); + if (top != 6) + { + return luaL_error(L, "intersection.raytriangle(rayOrig,rayDir,p0,p1,p2,ret)"); + } + struct vector3 *ro = check_userdata(L, 1); + struct vector3 *rd = check_userdata(L, 2); + struct vector3 *p0 = check_userdata(L, 3); + struct vector3 *p1 = check_userdata(L, 4); + struct vector3 *p2 = check_userdata(L, 5); + struct vector3 *inst = check_userdata(L, 6); + if (intersection_raytriangle(ro, rd, p0, p1, p2, inst) == NULL) + { + return 0; + } + return 1; +} + +static int +lrayAABB(lua_State *L) +{ + int top = lua_gettop(L); + if (top != 4) + { + return luaL_error(L, "intersection.rayAABB(rayOrig,rayDir,mins,maxs)"); + } + struct vector3 *ro = check_userdata(L, 1); + struct vector3 *rd = check_userdata(L, 2); + struct vector3 *mins = check_userdata(L, 3); + struct vector3 *maxs = check_userdata(L, 4); + int r = intersection_rayAABB(ro, rd, mins, maxs); + lua_pushboolean(L, r); + return 1; +} + +int luaopen_math3d(lua_State *L) +{ + luaL_checkversion(L); + lua_newtable(L); + vector3(L); + lua_setfield(L, -2, "vector3"); + quaternion(L); + lua_setfield(L, -2, "quaternion"); + matrix(L); + lua_setfield(L, -2, "matrix"); + vector4(L); + lua_setfield(L, -2, "vector4"); + plane(L); + lua_setfield(L, -2, "plane"); + luaL_Reg l[] = { + {"raytriangle", lraytriangle}, + {"rayAABB", lrayAABB}, + {NULL, NULL}, + }; + luaL_newlib(L, l); + lua_setfield(L, -2, "intersection"); + return 1; +} diff --git a/framework/lualib-src/lua-math3d/math3d-left.h b/framework/lualib-src/lua-math3d/math3d-left.h new file mode 100644 index 0000000..f75c7c8 --- /dev/null +++ b/framework/lualib-src/lua-math3d/math3d-left.h @@ -0,0 +1,836 @@ +// This is a rewrite version (in C) from Horde3D (utMath.h) , http://www.horde3d.org +// Math library +// +// Coordinate system is left-handed with positive y as up axis +// + +#ifndef ejoy3d_math_h +#define ejoy3d_math_h + +#include +#include +#include + +struct vector3 +{ + float x, y, z; +}; + +struct vector4 +{ + float x, y, z, w; +}; + +struct quaternion +{ + float x, y, z, w; +}; + +union matrix44 +{ + float c[4][4]; + float x[16]; +}; + +struct plane +{ + struct vector3 normal; + float dist; +}; + +// vector + +static inline float * +vector3_array(struct vector3 *v) +{ + return (float *)v; +} + +static inline float * +vector4_array(struct vector4 *v) +{ + return (float *)v; +} + +static inline float +vector3_dot(const struct vector3 *a, const struct vector3 *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +static inline struct vector3 * +vector3_cross(struct vector3 *v, const struct vector3 *a, const struct vector3 *b) +{ + float x = a->y * b->z - a->z * b->y; + float y = a->z * b->x - a->x * b->z; + float z = a->x * b->y - a->y * b->x; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline struct vector3 * +vector3_vector(struct vector3 *v, const struct vector3 *p1, const struct vector3 *p2) +{ + v->x = p1->x - p2->x; + v->y = p1->y - p2->y; + v->z = p1->z - p2->z; + + return v; +} + +static inline float +vector3_length(const struct vector3 *v) +{ + return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); +} + +static inline struct vector3 * +vector3_normalize(struct vector3 *v) +{ + float invLen = 1.0f / vector3_length(v); + v->x *= invLen; + v->y *= invLen; + v->z *= invLen; + + return v; +} + +static inline struct vector3 * +vector3_to_rotation(struct vector3 *v, const struct vector3 *r) +{ + // Assumes that the unrotated view vector is (0, 0, -1) + v->x = v->y = v->z = 0; + if (r->y != 0) + { + v->x = atan2f(r->y, sqrtf(r->x * r->x + r->z * r->z)); + } + if (r->x != 0 || r->z != 0) + { + v->y = atan2f(-r->x, -r->z); + } + + return v; +} + +static inline struct vector3 * +vector3_lerp(struct vector3 *v, const struct vector3 *a, const struct vector3 *b, float f) +{ + float x = a->x + (b->x - a->x) * f; + float y = a->y + (b->y - a->y) * f; + float z = a->z + (b->z - a->z) * f; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +// quaternion + +static inline struct quaternion * +quaternion_mul(struct quaternion *q, const struct quaternion *a, const struct quaternion *b) +{ + float x = a->y * b->z - a->z * b->y + b->x * a->w + a->x * b->w; + float y = a->z * b->x - a->x * b->z + b->y * a->w + a->y * b->w; + float z = a->x * b->y - a->y * b->x + b->z * a->w + a->z * b->w; + float w = a->w * b->w - (a->x * b->x + a->y * b->y + a->z * b->z); + + q->x = x; + q->y = y; + q->z = z; + q->w = w; + + return q; +} + +static inline struct quaternion * +quaternion_init(struct quaternion *q, float x, float y, float z) +{ + struct quaternion roll = {sinf(x * 0.5f), 0, 0, cosf(x * 0.5f)}; + struct quaternion pitch = {0, sinf(y * 0.5f), 0, cosf(y * 0.5f)}; + struct quaternion yaw = {0, 0, sinf(z * 0.5f), cosf(z * 0.5f)}; + + // Order: y * x * z + quaternion_mul(q, &pitch, &roll); + quaternion_mul(q, q, &yaw); + + return q; +} + +static inline struct quaternion * +quaternion_slerp(struct quaternion *q, const struct quaternion *a, const struct quaternion *b, float t) +{ + float cosTheta = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + if (cosTheta < 0) + { + cosTheta = -cosTheta; + q->x = -b->x; + q->y = -b->y; + q->z = -b->z; + q->w = -b->w; + } + else + { + *q = *b; + } + float scale0 = 1 - t, scale1 = t; + if ((1 - cosTheta) > 0.001f) + { + // use spherical interpolation + float theta = acosf(cosTheta); + float sinTheta = sinf(theta); + scale0 = sinf((1 - t) * theta) / sinTheta; + scale1 = sinf(t * theta) / sinTheta; + } + + q->x = a->x * scale0 + q->x * scale1; + q->y = a->y * scale0 + q->y * scale1; + q->z = a->z * scale0 + q->z * scale1; + q->w = a->w * scale0 + q->w * scale1; + + return q; +} + +static inline struct quaternion * +quaternion_nslerp(struct quaternion *q, const struct quaternion *a, const struct quaternion *b, float t) +{ + // Normalized linear quaternion interpolation + // Note: NLERP is faster than SLERP and commutative but does not yield constant velocity + + float cosTheta = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + + if (cosTheta < 0) + { + q->x = a->x + (-b->x - a->x) * t; + q->y = a->y + (-b->y - a->y) * t; + q->z = a->z + (-b->z - a->z) * t; + q->w = a->w + (-b->w - a->w) * t; + } + else + { + q->x = a->x + (b->x - a->x) * t; + q->y = a->y + (b->y - a->y) * t; + q->z = a->z + (b->z - a->z) * t; + q->w = a->w + (b->w - a->w) * t; + } + + float invLen = 1.0f / sqrtf(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w); + + q->x *= invLen; + q->y *= invLen; + q->z *= invLen; + q->w *= invLen; + + return q; +} + +static inline struct quaternion * +quaternion_inverted(struct quaternion *q) +{ + float len = q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w; + if (len > 0) + { + float invLen = -1.0f / len; + q->x *= invLen; + q->y *= invLen; + q->z *= invLen; + q->w *= invLen; + q->w = -q->w; + } + else + { + q->x = q->y = q->z = q->w = 0; + } + return q; +} + +// matrix 4*4 + +#define C m->c + +static inline union matrix44 * +matrix44_identity(union matrix44 *m) +{ + C[0][0] = 1; + C[1][0] = 0; + C[2][0] = 0; + C[3][0] = 0; + C[0][1] = 0; + C[1][1] = 1; + C[2][1] = 0; + C[3][1] = 0; + C[0][2] = 0; + C[1][2] = 0; + C[2][2] = 1; + C[3][2] = 0; + C[0][3] = 0; + C[1][3] = 0; + C[2][3] = 0; + C[3][3] = 1; + + return m; +} + +static inline union matrix44 * +matrix44_from_quaternion(union matrix44 *m, const struct quaternion *q) +{ + // Calculate coefficients + float x2 = q->x + q->x, y2 = q->y + q->y, z2 = q->z + q->z; + float xx = q->x * x2, xy = q->x * y2, xz = q->x * z2; + float yy = q->y * y2, yz = q->y * z2, zz = q->z * z2; + float wx = q->w * x2, wy = q->w * y2, wz = q->w * z2; + + C[0][0] = 1 - (yy + zz); + C[1][0] = xy + wz; + C[2][0] = xz - wy; + C[3][0] = 0; + C[0][1] = xy - wz; + C[1][1] = 1 - (xx + zz); + C[2][1] = yz + wx; + C[3][1] = 0; + C[0][2] = xz + wy; + C[1][2] = yz - wx; + C[2][2] = 1 - (xx + yy); + C[3][2] = 0; + C[0][3] = 0; + C[1][3] = 0; + C[2][3] = 0; + C[3][3] = 1; + + return m; +} + +static inline union matrix44 * +matrix44_trans(union matrix44 *m, float x, float y, float z) +{ + matrix44_identity(m); + C[3][0] = x; + C[3][1] = y; + C[3][2] = z; + + return m; +} + +static inline union matrix44 * +matrix44_scale(union matrix44 *m, float x, float y, float z) +{ + matrix44_identity(m); + C[0][0] = x; + C[1][1] = y; + C[2][2] = z; + + return m; +} + +static inline union matrix44 * +matrix44_rot(union matrix44 *m, float x, float y, float z) +{ + // Rotation order: YXZ [* Vector] + struct quaternion q; + quaternion_init(&q, x, y, z); + + return matrix44_from_quaternion(m, &q); +} + +#include +static inline union matrix44 * +matrix44_rot_axis(union matrix44 *m, const struct vector3 *axis, float angle) +{ + float t = sinf(angle * 0.5f); + float x = axis->x * t; + float y = axis->y * t; + float z = axis->z * t; + struct quaternion q = {x, y, z, cosf(angle * 0.5f)}; + + return matrix44_from_quaternion(m, &q); +} + +static inline union matrix44 * +matrix44_perspective(union matrix44 *m, float l, float r, float b, float t, float n, float f) +{ + matrix44_identity(m); + float *mx = m->x; + + mx[0] = 2 * n / (r - l); + mx[5] = 2 * n / (t - b); + mx[8] = -(r + l) / (r - l); + mx[9] = -(t + b) / (t - b); + mx[10] = (f + n) / (f - n); + mx[11] = 1; + mx[14] = -2 * f * n / (f - n); + mx[15] = 0; + + return m; +} + +static inline union matrix44 * +matrix44_ortho(union matrix44 *m, float l, float r, float b, float t, float n, float f) +{ + matrix44_identity(m); + float *mx = m->x; + + mx[0] = 2 / (r - l); + mx[5] = 2 / (t - b); + mx[10] = 2 / (f - n); + mx[12] = -(r + l) / (r - l); + mx[13] = -(t + b) / (t - b); + mx[14] = -(f + n) / (f - n); + + return m; +} + +static inline union matrix44 * +matrix44_fastmul43(union matrix44 *m, const union matrix44 *m1, const union matrix44 *m2) +{ + // Note: m may not be the same as m1 or m2 + + const float *m1x = m1->x; + const float *m2x = m2->x; + float *mx = m->x; + + mx[0] = m1x[0] * m2x[0] + m1x[1] * m2x[4] + m1x[2] * m2x[8]; + mx[1] = m1x[0] * m2x[1] + m1x[1] * m2x[5] + m1x[2] * m2x[9]; + mx[2] = m1x[0] * m2x[2] + m1x[1] * m2x[6] + m1x[2] * m2x[10]; + mx[3] = 0.0f; + + mx[4] = m1x[4] * m2x[0] + m1x[5] * m2x[4] + m1x[6] * m2x[8]; + mx[5] = m1x[4] * m2x[1] + m1x[5] * m2x[5] + m1x[6] * m2x[9]; + mx[6] = m1x[4] * m2x[2] + m1x[5] * m2x[6] + m1x[6] * m2x[10]; + mx[7] = 0.0f; + + mx[8] = m1x[8] * m2x[0] + m1x[9] * m2x[4] + m1x[10] * m2x[8]; + mx[9] = m1x[8] * m2x[1] + m1x[9] * m2x[5] + m1x[10] * m2x[9]; + mx[10] = m1x[8] * m2x[2] + m1x[9] * m2x[6] + m1x[10] * m2x[10]; + mx[11] = 0.0f; + + mx[12] = m1x[12] * m2x[0] + m1x[13] * m2x[4] + m1x[14] * m2x[8] + m1x[15] * m2x[12]; + mx[13] = m1x[12] * m2x[1] + m1x[13] * m2x[5] + m1x[14] * m2x[9] + m1x[15] * m2x[13]; + mx[14] = m1x[12] * m2x[1] + m1x[13] * m2x[6] + m1x[14] * m2x[10] + m1x[15] * m2x[14]; + mx[15] = 1.0f; + + return m; +} + +static inline union matrix44 * +matrix44_mul(union matrix44 *m, const union matrix44 *m1, const union matrix44 *m2) +{ + union matrix44 mf; + const float *m1x = m1->x; + const float *m2x = m2->x; + + mf.x[0] = m1x[0] * m2x[0] + m1x[1] * m2x[4] + m1x[2] * m2x[8] + m1x[3] * m2x[12]; + mf.x[1] = m1x[0] * m2x[1] + m1x[1] * m2x[5] + m1x[2] * m2x[9] + m1x[3] * m2x[13]; + mf.x[2] = m1x[0] * m2x[2] + m1x[1] * m2x[6] + m1x[2] * m2x[10] + m1x[3] * m2x[14]; + mf.x[3] = m1x[0] * m2x[3] + m1x[1] * m2x[7] + m1x[2] * m2x[11] + m1x[3] * m2x[15]; + + mf.x[4] = m1x[4] * m2x[0] + m1x[5] * m2x[4] + m1x[6] * m2x[8] + m1x[7] * m2x[12]; + mf.x[5] = m1x[4] * m2x[1] + m1x[5] * m2x[5] + m1x[6] * m2x[9] + m1x[7] * m2x[13]; + mf.x[6] = m1x[4] * m2x[2] + m1x[5] * m2x[6] + m1x[6] * m2x[10] + m1x[7] * m2x[14]; + mf.x[7] = m1x[4] * m2x[3] + m1x[5] * m2x[7] + m1x[6] * m2x[11] + m1x[7] * m2x[15]; + + mf.x[8] = m1x[8] * m2x[0] + m1x[9] * m2x[4] + m1x[10] * m2x[8] + m1x[11] * m2x[12]; + mf.x[9] = m1x[8] * m2x[1] + m1x[9] * m2x[5] + m1x[10] * m2x[9] + m1x[11] * m2x[13]; + mf.x[10] = m1x[8] * m2x[2] + m1x[9] * m2x[6] + m1x[10] * m2x[10] + m1x[11] * m2x[14]; + mf.x[11] = m1x[8] * m2x[3] + m1x[9] * m2x[7] + m1x[10] * m2x[11] + m1x[11] * m2x[15]; + + mf.x[12] = m1x[12] * m2x[0] + m1x[13] * m2x[4] + m1x[14] * m2x[8] + m1x[15] * m2x[12]; + mf.x[13] = m1x[12] * m2x[1] + m1x[13] * m2x[5] + m1x[14] * m2x[9] + m1x[15] * m2x[13]; + mf.x[14] = m1x[12] * m2x[2] + m1x[13] * m2x[6] + m1x[14] * m2x[10] + m1x[15] * m2x[14]; + mf.x[15] = m1x[12] * m2x[3] + m1x[13] * m2x[7] + m1x[14] * m2x[11] + m1x[15] * m2x[15]; + + *m = mf; + + return m; +} + +static inline struct vector3 * +vector3_mul(struct vector3 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[0][1] + v->z * C[0][2] + C[0][3]; + float y = v->x * C[1][0] + v->y * C[1][1] + v->z * C[1][2] + C[1][3]; + float z = v->x * C[2][0] + v->y * C[2][1] + v->z * C[2][2] + C[2][3]; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline struct vector4 * +vector4_mul(struct vector4 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[0][1] + v->z * C[0][2] + v->w * C[0][3]; + float y = v->x * C[1][0] + v->y * C[1][1] + v->z * C[1][2] + v->w * C[1][3]; + float z = v->x * C[2][0] + v->y * C[2][1] + v->z * C[2][2] + v->w * C[2][3]; + float w = v->x * C[3][0] + v->y * C[3][1] + v->z * C[3][2] + v->w * C[3][3]; + + v->x = x; + v->y = y; + v->z = z; + v->w = w; + return v; +} + +static inline struct vector3 * +vector3_mul33(struct vector3 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[0][1] + v->z * C[0][2]; + float y = v->x * C[1][0] + v->y * C[1][1] + v->z * C[1][2]; + float z = v->x * C[2][0] + v->y * C[2][1] + v->z * C[2][2]; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline union matrix44 * +matrix44_transposed(union matrix44 *m) +{ + int x, y; + for (y = 0; y < 4; ++y) + { + for (x = y + 1; x < 4; ++x) + { + float tmp = C[x][y]; + C[x][y] = C[y][x]; + C[y][x] = tmp; + } + } + + return m; +} + +static inline float +matrix44_determinant(const union matrix44 *m) +{ + return C[0][3] * C[1][2] * C[2][1] * C[3][0] - C[0][2] * C[1][3] * C[2][1] * C[3][0] - C[0][3] * C[1][1] * C[2][2] * C[3][0] + C[0][1] * C[1][3] * C[2][2] * C[3][0] + + C[0][2] * C[1][1] * C[2][3] * C[3][0] - C[0][1] * C[1][2] * C[2][3] * C[3][0] - C[0][3] * C[1][2] * C[2][0] * C[3][1] + C[0][2] * C[1][3] * C[2][0] * C[3][1] + + C[0][3] * C[1][0] * C[2][2] * C[3][1] - C[0][0] * C[1][3] * C[2][2] * C[3][1] - C[0][2] * C[1][0] * C[2][3] * C[3][1] + C[0][0] * C[1][2] * C[2][3] * C[3][1] + + C[0][3] * C[1][1] * C[2][0] * C[3][2] - C[0][1] * C[1][3] * C[2][0] * C[3][2] - C[0][3] * C[1][0] * C[2][1] * C[3][2] + C[0][0] * C[1][3] * C[2][1] * C[3][2] + + C[0][1] * C[1][0] * C[2][3] * C[3][2] - C[0][0] * C[1][1] * C[2][3] * C[3][2] - C[0][2] * C[1][1] * C[2][0] * C[3][3] + C[0][1] * C[1][2] * C[2][0] * C[3][3] + + C[0][2] * C[1][0] * C[2][1] * C[3][3] - C[0][0] * C[1][2] * C[2][1] * C[3][3] - C[0][1] * C[1][0] * C[2][2] * C[3][3] + C[0][0] * C[1][1] * C[2][2] * C[3][3]; +} + +static inline union matrix44 * +matrix44_inverted(union matrix44 *dst, const union matrix44 *m) +{ + float d = matrix44_determinant(m); + if (d == 0) + { + *dst = *m; + return dst; + } + d = 1.0f / d; + + dst->c[0][0] = d * (C[1][2] * C[2][3] * C[3][1] - C[1][3] * C[2][2] * C[3][1] + C[1][3] * C[2][1] * C[3][2] - C[1][1] * C[2][3] * C[3][2] - C[1][2] * C[2][1] * C[3][3] + C[1][1] * C[2][2] * C[3][3]); + dst->c[0][1] = d * (C[0][3] * C[2][2] * C[3][1] - C[0][2] * C[2][3] * C[3][1] - C[0][3] * C[2][1] * C[3][2] + C[0][1] * C[2][3] * C[3][2] + C[0][2] * C[2][1] * C[3][3] - C[0][1] * C[2][2] * C[3][3]); + dst->c[0][2] = d * (C[0][2] * C[1][3] * C[3][1] - C[0][3] * C[1][2] * C[3][1] + C[0][3] * C[1][1] * C[3][2] - C[0][1] * C[1][3] * C[3][2] - C[0][2] * C[1][1] * C[3][3] + C[0][1] * C[1][2] * C[3][3]); + dst->c[0][3] = d * (C[0][3] * C[1][2] * C[2][1] - C[0][2] * C[1][3] * C[2][1] - C[0][3] * C[1][1] * C[2][2] + C[0][1] * C[1][3] * C[2][2] + C[0][2] * C[1][1] * C[2][3] - C[0][1] * C[1][2] * C[2][3]); + dst->c[1][0] = d * (C[1][3] * C[2][2] * C[3][0] - C[1][2] * C[2][3] * C[3][0] - C[1][3] * C[2][0] * C[3][2] + C[1][0] * C[2][3] * C[3][2] + C[1][2] * C[2][0] * C[3][3] - C[1][0] * C[2][2] * C[3][3]); + dst->c[1][1] = d * (C[0][2] * C[2][3] * C[3][0] - C[0][3] * C[2][2] * C[3][0] + C[0][3] * C[2][0] * C[3][2] - C[0][0] * C[2][3] * C[3][2] - C[0][2] * C[2][0] * C[3][3] + C[0][0] * C[2][2] * C[3][3]); + dst->c[1][2] = d * (C[0][3] * C[1][2] * C[3][0] - C[0][2] * C[1][3] * C[3][0] - C[0][3] * C[1][0] * C[3][2] + C[0][0] * C[1][3] * C[3][2] + C[0][2] * C[1][0] * C[3][3] - C[0][0] * C[1][2] * C[3][3]); + dst->c[1][3] = d * (C[0][2] * C[1][3] * C[2][0] - C[0][3] * C[1][2] * C[2][0] + C[0][3] * C[1][0] * C[2][2] - C[0][0] * C[1][3] * C[2][2] - C[0][2] * C[1][0] * C[2][3] + C[0][0] * C[1][2] * C[2][3]); + dst->c[2][0] = d * (C[1][1] * C[2][3] * C[3][0] - C[1][3] * C[2][1] * C[3][0] + C[1][3] * C[2][0] * C[3][1] - C[1][0] * C[2][3] * C[3][1] - C[1][1] * C[2][0] * C[3][3] + C[1][0] * C[2][1] * C[3][3]); + dst->c[2][1] = d * (C[0][3] * C[2][1] * C[3][0] - C[0][1] * C[2][3] * C[3][0] - C[0][3] * C[2][0] * C[3][1] + C[0][0] * C[2][3] * C[3][1] + C[0][1] * C[2][0] * C[3][3] - C[0][0] * C[2][1] * C[3][3]); + dst->c[2][2] = d * (C[0][1] * C[1][3] * C[3][0] - C[0][3] * C[1][1] * C[3][0] + C[0][3] * C[1][0] * C[3][1] - C[0][0] * C[1][3] * C[3][1] - C[0][1] * C[1][0] * C[3][3] + C[0][0] * C[1][1] * C[3][3]); + dst->c[2][3] = d * (C[0][3] * C[1][1] * C[2][0] - C[0][1] * C[1][3] * C[2][0] - C[0][3] * C[1][0] * C[2][1] + C[0][0] * C[1][3] * C[2][1] + C[0][1] * C[1][0] * C[2][3] - C[0][0] * C[1][1] * C[2][3]); + dst->c[3][0] = d * (C[1][2] * C[2][1] * C[3][0] - C[1][1] * C[2][2] * C[3][0] - C[1][2] * C[2][0] * C[3][1] + C[1][0] * C[2][2] * C[3][1] + C[1][1] * C[2][0] * C[3][2] - C[1][0] * C[2][1] * C[3][2]); + dst->c[3][1] = d * (C[0][1] * C[2][2] * C[3][0] - C[0][2] * C[2][1] * C[3][0] + C[0][2] * C[2][0] * C[3][1] - C[0][0] * C[2][2] * C[3][1] - C[0][1] * C[2][0] * C[3][2] + C[0][0] * C[2][1] * C[3][2]); + dst->c[3][2] = d * (C[0][2] * C[1][1] * C[3][0] - C[0][1] * C[1][2] * C[3][0] - C[0][2] * C[1][0] * C[3][1] + C[0][0] * C[1][2] * C[3][1] + C[0][1] * C[1][0] * C[3][2] - C[0][0] * C[1][1] * C[3][2]); + dst->c[3][3] = d * (C[0][1] * C[1][2] * C[2][0] - C[0][2] * C[1][1] * C[2][0] + C[0][2] * C[1][0] * C[2][1] - C[0][0] * C[1][2] * C[2][1] - C[0][1] * C[1][0] * C[2][2] + C[0][0] * C[1][1] * C[2][2]); + + return dst; +} + +static inline struct vector3 * +matrix44_gettrans(const union matrix44 *m, struct vector3 *trans) +{ + // Getting translation is trivial + trans->x = C[3][0]; + trans->y = C[3][1]; + trans->z = C[3][2]; + + return trans; +} + +static inline struct vector3 * +matrix44_getscale(const union matrix44 *m, struct vector3 *scale) +{ + // Scale is length of columns + scale->x = sqrtf(C[0][0] * C[0][0] + C[0][1] * C[0][1] + C[0][2] * C[0][2]); + scale->y = sqrtf(C[1][0] * C[1][0] + C[1][1] * C[1][1] + C[1][2] * C[1][2]); + scale->z = sqrtf(C[2][0] * C[2][0] + C[2][1] * C[2][1] + C[2][2] * C[2][2]); + + return scale; +} + +// NOTICE: Huanzai +// this function may be need to convert from right to left handed coordinate +static inline void +matrix44_decompose(const union matrix44 *m, struct vector3 *trans, struct vector3 *rot, struct vector3 *scale) +{ + matrix44_gettrans(m, trans); + matrix44_getscale(m, scale); + + if (scale->x == 0 || scale->y == 0 || scale->z == 0) + { + rot->x = 0; + rot->y = 0; + rot->z = 0; + return; + } + + // Detect negative scale with determinant and flip one arbitrary axis + if (matrix44_determinant(m) < 0) + scale->x = -scale->x; + + // Combined rotation matrix YXZ + // + // Cos[y]*Cos[z]+Sin[x]*Sin[y]*Sin[z] Cos[z]*Sin[x]*Sin[y]-Cos[y]*Sin[z] Cos[x]*Sin[y] + // Cos[x]*Sin[z] Cos[x]*Cos[z] -Sin[x] + // -Cos[z]*Sin[y]+Cos[y]*Sin[x]*Sin[z] Cos[y]*Cos[z]*Sin[x]+Sin[y]*Sin[z] Cos[x]*Cos[y] + + rot->x = asinf(-C[2][1] / scale->z); + + // Special case: Cos[x] == 0 (when Sin[x] is +/-1) + float f = fabsf(C[2][1] / scale->z); + + if (f > 0.999f && f < 1.001f) + { + // Pin arbitrarily one of y or z to zero + // Mathematical equivalent of gimbal lock + rot->y = 0; + + // Now: Cos[x] = 0, Sin[x] = +/-1, Cos[y] = 1, Sin[y] = 0 + // => m[0][0] = Cos[z] and m[1][0] = Sin[z] + rot->z = atan2f(-C[1][0] / scale->y, C[0][0] / scale->x); + } + else + { + // Standard case + rot->y = atan2f(C[2][0] / scale->z, C[2][2] / scale->z); + rot->z = atan2f(C[0][1] / scale->x, C[1][1] / scale->y); + } +} + +static inline float * +matrix44_to33(const union matrix44 *m, float m33[9]) +{ + m33[0] = C[0][0]; + m33[1] = C[0][1]; + m33[2] = C[0][2]; + m33[3] = C[1][0]; + m33[4] = C[1][1]; + m33[5] = C[1][2]; + m33[6] = C[2][0]; + m33[7] = C[2][1]; + m33[8] = C[2][2]; + + return m33; +} + +#undef C + +// plane + +static inline struct plane * +plane_init(struct plane *p, const struct vector3 *normal, float d) +{ + p->normal = *normal; + // normalize + float invLen = 1.0f / vector3_length(normal); + p->normal.x *= invLen; + p->normal.y *= invLen; + p->normal.z *= invLen; + p->dist = d * invLen; + + return p; +} + +static inline struct plane * +plane_init_dot3(struct plane *p, const struct vector3 *v0, const struct vector3 *v1, const struct vector3 *v2) +{ + struct vector3 a, b; + vector3_vector(&a, v1, v0); + vector3_vector(&b, v2, v0); + + vector3_cross(&p->normal, &a, &b); + vector3_normalize(&p->normal); + p->dist = -vector3_dot(&p->normal, v0); + + return p; +} + +static inline float +plane_dist(const struct plane *p, const struct vector3 *v) +{ + float d = vector3_dot(&p->normal, v); + return d + p->dist; +} + +// Intersection + +static inline struct vector3 * +intersection_raytriangle(const struct vector3 *rayOrig, const struct vector3 *rayDir, + const struct vector3 *vert0, const struct vector3 *vert1, const struct vector3 *vert2, + struct vector3 *intsPoint) +{ + // Idea: Tomas Moeller and Ben Trumbore + // in Fast, Minimum Storage Ray/Triangle Intersection + + // Find vectors for two edges sharing vert0 + struct vector3 edge1, edge2; + vector3_vector(&edge1, vert1, vert0); + vector3_vector(&edge2, vert2, vert0); + + // Begin calculating determinant - also used to calculate U parameter + struct vector3 pvec; + vector3_cross(&pvec, rayDir, &edge2); + + // If determinant is near zero, ray lies in plane of triangle + float det = vector3_dot(&edge1, &pvec); + + // *** Culling branch *** + /*if( det < FLT_EPSILON ) + return NULL; + + // Calculate distance from vert0 to ray origin + struct vector3 tvec; + vector3_vector(&tvec, rayOrig, &vert0); + + // Calculate U parameter and test bounds + float u = vector3_dot(&tvec, &pvec); + if (u < 0 || u > det ) + return NULL; + + // Prepare to test V parameter + struct vector3 qvec; + vector3_cross(&qvec, &tvec, &edge1); + + // Calculate V parameter and test bounds + float v = vector3_dot(rayDir, &qvec); + if (v < 0 || u + v > det ) + return NULL; + + // Calculate t, scale parameters, ray intersects triangle + float t = vector3_dot(&edge2, &qvec ) / det;*/ + + // *** Non-culling branch *** + if (det > -FLT_EPSILON && det < FLT_EPSILON) + return 0; + float inv_det = 1.0f / det; + + // Calculate distance from vert0 to ray origin + struct vector3 tvec; + vector3_vector(&tvec, rayOrig, vert0); + + // Calculate U parameter and test bounds + float u = vector3_dot(&tvec, &pvec) * inv_det; + if (u < 0.0f || u > 1.0f) + return 0; + + // Prepare to test V parameter + struct vector3 qvec; + vector3_cross(&qvec, &tvec, &edge1); + + // Calculate V parameter and test bounds + float v = vector3_dot(rayDir, &qvec) * inv_det; + if (v < 0.0f || u + v > 1.0f) + return 0; + + // Calculate t, ray intersects triangle + float t = vector3_dot(&edge2, &qvec) * inv_det; + + // Calculate intersection point and test ray length and direction + intsPoint->x = rayOrig->x + rayDir->x * t; + intsPoint->y = rayOrig->y + rayDir->y * t; + intsPoint->z = rayOrig->z + rayDir->z * t; + + struct vector3 vec; + vector3_vector(&vec, intsPoint, rayOrig); + if (vector3_dot(&vec, rayDir) < 0 || vector3_length(&vec) > vector3_length(rayDir)) + return NULL; + + return intsPoint; +} + +static inline float +minf(float a, float b) +{ + return a < b ? a : b; +} + +static inline float +maxf(float a, float b) +{ + return a > b ? a : b; +} + +static inline int +intersection_rayAABB(const struct vector3 *rayOrig, const struct vector3 *rayDir, + const struct vector3 *mins, const struct vector3 *maxs) +{ + // SLAB based optimized ray/AABB intersection routine + // Idea taken from http://ompf.org/ray/ + + float l1 = (mins->x - rayOrig->x) / rayDir->x; + float l2 = (maxs->x - rayOrig->x) / rayDir->x; + float lmin = minf(l1, l2); + float lmax = maxf(l1, l2); + + l1 = (mins->y - rayOrig->y) / rayDir->y; + l2 = (maxs->y - rayOrig->y) / rayDir->y; + lmin = maxf(minf(l1, l2), lmin); + lmax = minf(maxf(l1, l2), lmax); + + l1 = (mins->z - rayOrig->z) / rayDir->z; + l2 = (maxs->z - rayOrig->z) / rayDir->z; + lmin = maxf(minf(l1, l2), lmin); + lmax = minf(maxf(l1, l2), lmax); + + if ((lmax >= 0.0f) & (lmax >= lmin)) + { + // Consider length + const struct vector3 rayDest = {rayOrig->x + rayDir->x, rayOrig->y + rayDir->y, rayOrig->z + rayDir->z}; + const struct vector3 rayMins = {minf(rayDest.x, rayOrig->x), minf(rayDest.y, rayOrig->y), minf(rayDest.z, rayOrig->z)}; + const struct vector3 rayMaxs = {maxf(rayDest.x, rayOrig->x), maxf(rayDest.y, rayOrig->y), maxf(rayDest.z, rayOrig->z)}; + return (rayMins.x < maxs->x) && (rayMaxs.x > mins->x) && + (rayMins.y < maxs->y) && (rayMaxs.y > mins->y) && + (rayMins.z < maxs->z) && (rayMaxs.z > mins->z); + } + else + { + return 0; + } +} + +static inline float +vector3_distAABB(const struct vector3 *pos, const struct vector3 *mins, const struct vector3 *maxs) +{ + struct vector3 center; + struct vector3 extent; + center.x = (mins->x + maxs->x) * 0.5f; + center.y = (mins->y + maxs->y) * 0.5f; + center.z = (mins->z + maxs->z) * 0.5f; + + extent.x = (maxs->x - mins->x) * 0.5f; + extent.y = (maxs->y - mins->y) * 0.5f; + extent.z = (maxs->z - mins->z) * 0.5f; + + struct vector3 nearestVec; + nearestVec.x = maxf(0, fabsf(pos->x - center.x) - extent.x); + nearestVec.y = maxf(0, fabsf(pos->y - center.y) - extent.y); + nearestVec.z = maxf(0, fabsf(pos->z - center.z) - extent.z); + + return vector3_length(&nearestVec); +} + +#endif diff --git a/framework/lualib-src/lua-math3d/math3d-right.h b/framework/lualib-src/lua-math3d/math3d-right.h new file mode 100644 index 0000000..e01fcbb --- /dev/null +++ b/framework/lualib-src/lua-math3d/math3d-right.h @@ -0,0 +1,835 @@ +// This is a rewrite version (in C) from Horde3D (utMath.h) , http://www.horde3d.org +// Math library +// +// Coordinate system is right-handed with positive y as up axis +// + +#ifndef ejoy3d_math_h +#define ejoy3d_math_h + +#include +#include +#include + +struct vector3 +{ + float x, y, z; +}; + +struct vector4 +{ + float x, y, z, w; +}; + +struct quaternion +{ + float x, y, z, w; +}; + +union matrix44 +{ + float c[4][4]; + float x[16]; +}; + +struct plane +{ + struct vector3 normal; + float dist; +}; + +// vector + +static inline float * +vector3_array(struct vector3 *v) +{ + return (float *)v; +} + +static inline float * +vector4_array(struct vector4 *v) +{ + return (float *)v; +} + +static inline float +vector3_dot(const struct vector3 *a, const struct vector3 *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +static inline struct vector3 * +vector3_cross(struct vector3 *v, const struct vector3 *a, const struct vector3 *b) +{ + float x = a->y * b->z - a->z * b->y; + float y = a->z * b->x - a->x * b->z; + float z = a->x * b->y - a->y * b->x; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline struct vector3 * +vector3_vector(struct vector3 *v, const struct vector3 *p1, const struct vector3 *p2) +{ + v->x = p1->x - p2->x; + v->y = p1->y - p2->y; + v->z = p1->z - p2->z; + + return v; +} + +static inline float +vector3_length(const struct vector3 *v) +{ + return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); +} + +static inline struct vector3 * +vector3_normalize(struct vector3 *v) +{ + float invLen = 1.0f / vector3_length(v); + v->x *= invLen; + v->y *= invLen; + v->z *= invLen; + + return v; +} + +static inline struct vector3 * +vector3_to_rotation(struct vector3 *v, const struct vector3 *r) +{ + // Assumes that the unrotated view vector is (0, 0, -1) + v->x = v->y = v->z = 0; + if (r->y != 0) + { + v->x = atan2f(r->y, sqrtf(r->x * r->x + r->z * r->z)); + } + if (r->x != 0 || r->z != 0) + { + v->y = atan2f(-r->x, -r->z); + } + + return v; +} + +static inline struct vector3 * +vector3_lerp(struct vector3 *v, const struct vector3 *a, const struct vector3 *b, float f) +{ + float x = a->x + (b->x - a->x) * f; + float y = a->y + (b->y - a->y) * f; + float z = a->z + (b->z - a->z) * f; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +// quaternion + +static inline struct quaternion * +quaternion_mul(struct quaternion *q, const struct quaternion *a, const struct quaternion *b) +{ + float x = a->y * b->z - a->z * b->y + b->x * a->w + a->x * b->w; + float y = a->z * b->x - a->x * b->z + b->y * a->w + a->y * b->w; + float z = a->x * b->y - a->y * b->x + b->z * a->w + a->z * b->w; + float w = a->w * b->w - (a->x * b->x + a->y * b->y + a->z * b->z); + + q->x = x; + q->y = y; + q->z = z; + q->w = w; + + return q; +} + +static inline struct quaternion * +quaternion_init(struct quaternion *q, float x, float y, float z) +{ + struct quaternion roll = {sinf(x * 0.5f), 0, 0, cosf(x * 0.5f)}; + struct quaternion pitch = {0, sinf(y * 0.5f), 0, cosf(y * 0.5f)}; + struct quaternion yaw = {0, 0, sinf(z * 0.5f), cosf(z * 0.5f)}; + + // Order: y * x * z + quaternion_mul(q, &pitch, &roll); + quaternion_mul(q, q, &yaw); + + return q; +} + +static inline struct quaternion * +quaternion_slerp(struct quaternion *q, const struct quaternion *a, const struct quaternion *b, float t) +{ + float cosTheta = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + if (cosTheta < 0) + { + cosTheta = -cosTheta; + q->x = -b->x; + q->y = -b->y; + q->z = -b->z; + q->w = -b->w; + } + else + { + *q = *b; + } + float scale0 = 1 - t, scale1 = t; + if ((1 - cosTheta) > 0.001f) + { + // use spherical interpolation + float theta = acosf(cosTheta); + float sinTheta = sinf(theta); + scale0 = sinf((1 - t) * theta) / sinTheta; + scale1 = sinf(t * theta) / sinTheta; + } + + q->x = a->x * scale0 + q->x * scale1; + q->y = a->y * scale0 + q->y * scale1; + q->z = a->z * scale0 + q->z * scale1; + q->w = a->w * scale0 + q->w * scale1; + + return q; +} + +static inline struct quaternion * +quaternion_nslerp(struct quaternion *q, const struct quaternion *a, const struct quaternion *b, float t) +{ + // Normalized linear quaternion interpolation + // Note: NLERP is faster than SLERP and commutative but does not yield constant velocity + + float cosTheta = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + + if (cosTheta < 0) + { + q->x = a->x + (-b->x - a->x) * t; + q->y = a->y + (-b->y - a->y) * t; + q->z = a->z + (-b->z - a->z) * t; + q->w = a->w + (-b->w - a->w) * t; + } + else + { + q->x = a->x + (b->x - a->x) * t; + q->y = a->y + (b->y - a->y) * t; + q->z = a->z + (b->z - a->z) * t; + q->w = a->w + (b->w - a->w) * t; + } + + float invLen = 1.0f / sqrtf(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w); + + q->x *= invLen; + q->y *= invLen; + q->z *= invLen; + q->w *= invLen; + + return q; +} + +static inline struct quaternion * +quaternion_inverted(struct quaternion *q) +{ + float len = q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w; + if (len > 0) + { + float invLen = -1.0f / len; + q->x *= invLen; + q->y *= invLen; + q->z *= invLen; + q->w *= invLen; + q->w = -q->w; + } + else + { + q->x = q->y = q->z = q->w = 0; + } + return q; +} + +// matrix 4*4 + +#define C m->c + +static inline union matrix44 * +matrix44_identity(union matrix44 *m) +{ + C[0][0] = 1; + C[1][0] = 0; + C[2][0] = 0; + C[3][0] = 0; + C[0][1] = 0; + C[1][1] = 1; + C[2][1] = 0; + C[3][1] = 0; + C[0][2] = 0; + C[1][2] = 0; + C[2][2] = 1; + C[3][2] = 0; + C[0][3] = 0; + C[1][3] = 0; + C[2][3] = 0; + C[3][3] = 1; + + return m; +} + +static inline union matrix44 * +matrix44_from_quaternion(union matrix44 *m, const struct quaternion *q) +{ + // Calculate coefficients + float x2 = q->x + q->x, y2 = q->y + q->y, z2 = q->z + q->z; + float xx = q->x * x2, xy = q->x * y2, xz = q->x * z2; + float yy = q->y * y2, yz = q->y * z2, zz = q->z * z2; + float wx = q->w * x2, wy = q->w * y2, wz = q->w * z2; + + C[0][0] = 1 - (yy + zz); + C[1][0] = xy - wz; + C[2][0] = xz + wy; + C[3][0] = 0; + C[0][1] = xy + wz; + C[1][1] = 1 - (xx + zz); + C[2][1] = yz - wx; + C[3][1] = 0; + C[0][2] = xz - wy; + C[1][2] = yz + wx; + C[2][2] = 1 - (xx + yy); + C[3][2] = 0; + C[0][3] = 0; + C[1][3] = 0; + C[2][3] = 0; + C[3][3] = 1; + + return m; +} + +static inline union matrix44 * +matrix44_trans(union matrix44 *m, float x, float y, float z) +{ + matrix44_identity(m); + C[3][0] = x; + C[3][1] = y; + C[3][2] = z; + + return m; +} + +static inline union matrix44 * +matrix44_scale(union matrix44 *m, float x, float y, float z) +{ + matrix44_identity(m); + C[0][0] = x; + C[1][1] = y; + C[2][2] = z; + + return m; +} + +static inline union matrix44 * +matrix44_rot(union matrix44 *m, float x, float y, float z) +{ + // Rotation order: YXZ [* Vector] + struct quaternion q; + quaternion_init(&q, x, y, z); + + return matrix44_from_quaternion(m, &q); +} + +static inline union matrix44 * +matrix44_rot_axis(union matrix44 *m, const struct vector3 *axis, float angle) +{ + float t = sinf(angle * 0.5f); + float x = axis->x * t; + float y = axis->y * t; + float z = axis->z * t; + struct quaternion q = {x, y, z, cosf(angle * 0.5f)}; + + return matrix44_from_quaternion(m, &q); +} + +static inline union matrix44 * +matrix44_perspective(union matrix44 *m, float l, float r, float b, float t, float n, float f) +{ + matrix44_identity(m); + float *mx = m->x; + + mx[0] = 2 * n / (r - l); + mx[5] = 2 * n / (t - b); + mx[8] = (r + l) / (r - l); + mx[9] = (t + b) / (t - b); + mx[10] = -(f + n) / (f - n); + mx[11] = -1; + mx[14] = -2 * f * n / (f - n); + mx[15] = 0; + + return m; +} + +static inline union matrix44 * +matrix44_ortho(union matrix44 *m, float l, float r, float b, float t, float n, float f) +{ + matrix44_identity(m); + float *mx = m->x; + + mx[0] = 2 / (r - l); + mx[5] = 2 / (t - b); + mx[10] = -2 / (f - n); + mx[12] = -(r + l) / (r - l); + mx[13] = -(t + b) / (t - b); + mx[14] = -(f + n) / (f - n); + + return m; +} + +static inline union matrix44 * +matrix44_fastmul43(union matrix44 *m, const union matrix44 *m1, const union matrix44 *m2) +{ + // Note: m may not be the same as m1 or m2 + + const float *m1x = m1->x; + const float *m2x = m2->x; + float *mx = m->x; + + mx[0] = m1x[0] * m2x[0] + m1x[4] * m2x[1] + m1x[8] * m2x[2]; + mx[1] = m1x[1] * m2x[0] + m1x[5] * m2x[1] + m1x[9] * m2x[2]; + mx[2] = m1x[2] * m2x[0] + m1x[6] * m2x[1] + m1x[10] * m2x[2]; + mx[3] = 0.0f; + + mx[4] = m1x[0] * m2x[4] + m1x[4] * m2x[5] + m1x[8] * m2x[6]; + mx[5] = m1x[1] * m2x[4] + m1x[5] * m2x[5] + m1x[9] * m2x[6]; + mx[6] = m1x[2] * m2x[4] + m1x[6] * m2x[5] + m1x[10] * m2x[6]; + mx[7] = 0.0f; + + mx[8] = m1x[0] * m2x[8] + m1x[4] * m2x[9] + m1x[8] * m2x[10]; + mx[9] = m1x[1] * m2x[8] + m1x[5] * m2x[9] + m1x[9] * m2x[10]; + mx[10] = m1x[2] * m2x[8] + m1x[6] * m2x[9] + m1x[10] * m2x[10]; + mx[11] = 0.0f; + + mx[12] = m1x[0] * m2x[12] + m1x[4] * m2x[13] + m1x[8] * m2x[14] + m1x[12] * m2x[15]; + mx[13] = m1x[1] * m2x[12] + m1x[5] * m2x[13] + m1x[9] * m2x[14] + m1x[13] * m2x[15]; + mx[14] = m1x[2] * m2x[12] + m1x[6] * m2x[13] + m1x[10] * m2x[14] + m1x[14] * m2x[15]; + mx[15] = 1.0f; + + return m; +} + +static inline union matrix44 * +matrix44_mul(union matrix44 *m, const union matrix44 *m1, const union matrix44 *m2) +{ + union matrix44 mf; + const float *m1x = m1->x; + const float *m2x = m2->x; + + mf.x[0] = m1x[0] * m2x[0] + m1x[4] * m2x[1] + m1x[8] * m2x[2] + m1x[12] * m2x[3]; + mf.x[1] = m1x[1] * m2x[0] + m1x[5] * m2x[1] + m1x[9] * m2x[2] + m1x[13] * m2x[3]; + mf.x[2] = m1x[2] * m2x[0] + m1x[6] * m2x[1] + m1x[10] * m2x[2] + m1x[14] * m2x[3]; + mf.x[3] = m1x[3] * m2x[0] + m1x[7] * m2x[1] + m1x[11] * m2x[2] + m1x[15] * m2x[3]; + + mf.x[4] = m1x[0] * m2x[4] + m1x[4] * m2x[5] + m1x[8] * m2x[6] + m1x[12] * m2x[7]; + mf.x[5] = m1x[1] * m2x[4] + m1x[5] * m2x[5] + m1x[9] * m2x[6] + m1x[13] * m2x[7]; + mf.x[6] = m1x[2] * m2x[4] + m1x[6] * m2x[5] + m1x[10] * m2x[6] + m1x[14] * m2x[7]; + mf.x[7] = m1x[3] * m2x[4] + m1x[7] * m2x[5] + m1x[11] * m2x[6] + m1x[15] * m2x[7]; + + mf.x[8] = m1x[0] * m2x[8] + m1x[4] * m2x[9] + m1x[8] * m2x[10] + m1x[12] * m2x[11]; + mf.x[9] = m1x[1] * m2x[8] + m1x[5] * m2x[9] + m1x[9] * m2x[10] + m1x[13] * m2x[11]; + mf.x[10] = m1x[2] * m2x[8] + m1x[6] * m2x[9] + m1x[10] * m2x[10] + m1x[14] * m2x[11]; + mf.x[11] = m1x[3] * m2x[8] + m1x[7] * m2x[9] + m1x[11] * m2x[10] + m1x[15] * m2x[11]; + + mf.x[12] = m1x[0] * m2x[12] + m1x[4] * m2x[13] + m1x[8] * m2x[14] + m1x[12] * m2x[15]; + mf.x[13] = m1x[1] * m2x[12] + m1x[5] * m2x[13] + m1x[9] * m2x[14] + m1x[13] * m2x[15]; + mf.x[14] = m1x[2] * m2x[12] + m1x[6] * m2x[13] + m1x[10] * m2x[14] + m1x[14] * m2x[15]; + mf.x[15] = m1x[3] * m2x[12] + m1x[7] * m2x[13] + m1x[11] * m2x[14] + m1x[15] * m2x[15]; + + *m = mf; + + return m; +} + +// vector * matrix + +static inline struct vector3 * +vector3_mul(struct vector3 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[1][0] + v->z * C[2][0] + C[3][0]; + float y = v->x * C[0][1] + v->y * C[1][1] + v->z * C[2][1] + C[3][1]; + float z = v->x * C[0][2] + v->y * C[1][2] + v->z * C[2][2] + C[3][2]; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline struct vector4 * +vector4_mul(struct vector4 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[1][0] + v->z * C[2][0] + v->w * C[3][0]; + float y = v->x * C[0][1] + v->y * C[1][1] + v->z * C[2][1] + v->w * C[3][1]; + float z = v->x * C[0][2] + v->y * C[1][2] + v->z * C[2][2] + v->w * C[3][2]; + float w = v->x * C[0][3] + v->y * C[1][3] + v->z * C[2][3] + v->w * C[3][3]; + + v->x = x; + v->y = y; + v->z = z; + v->w = w; + return v; +} + +static inline struct vector3 * +vector3_mul33(struct vector3 *v, const union matrix44 *m) +{ + float x = v->x * C[0][0] + v->y * C[1][0] + v->z * C[2][0]; + float y = v->x * C[0][1] + v->y * C[1][1] + v->z * C[2][1]; + float z = v->x * C[0][2] + v->y * C[1][2] + v->z * C[2][2]; + + v->x = x; + v->y = y; + v->z = z; + + return v; +} + +static inline union matrix44 * +matrix44_transposed(union matrix44 *m) +{ + int x, y; + for (y = 0; y < 4; ++y) + { + for (x = y + 1; x < 4; ++x) + { + float tmp = C[x][y]; + C[x][y] = C[y][x]; + C[y][x] = tmp; + } + } + + return m; +} + +static inline float +matrix44_determinant(const union matrix44 *m) +{ + return C[0][3] * C[1][2] * C[2][1] * C[3][0] - C[0][2] * C[1][3] * C[2][1] * C[3][0] - C[0][3] * C[1][1] * C[2][2] * C[3][0] + C[0][1] * C[1][3] * C[2][2] * C[3][0] + + C[0][2] * C[1][1] * C[2][3] * C[3][0] - C[0][1] * C[1][2] * C[2][3] * C[3][0] - C[0][3] * C[1][2] * C[2][0] * C[3][1] + C[0][2] * C[1][3] * C[2][0] * C[3][1] + + C[0][3] * C[1][0] * C[2][2] * C[3][1] - C[0][0] * C[1][3] * C[2][2] * C[3][1] - C[0][2] * C[1][0] * C[2][3] * C[3][1] + C[0][0] * C[1][2] * C[2][3] * C[3][1] + + C[0][3] * C[1][1] * C[2][0] * C[3][2] - C[0][1] * C[1][3] * C[2][0] * C[3][2] - C[0][3] * C[1][0] * C[2][1] * C[3][2] + C[0][0] * C[1][3] * C[2][1] * C[3][2] + + C[0][1] * C[1][0] * C[2][3] * C[3][2] - C[0][0] * C[1][1] * C[2][3] * C[3][2] - C[0][2] * C[1][1] * C[2][0] * C[3][3] + C[0][1] * C[1][2] * C[2][0] * C[3][3] + + C[0][2] * C[1][0] * C[2][1] * C[3][3] - C[0][0] * C[1][2] * C[2][1] * C[3][3] - C[0][1] * C[1][0] * C[2][2] * C[3][3] + C[0][0] * C[1][1] * C[2][2] * C[3][3]; +} + +static inline union matrix44 * +matrix44_inverted(union matrix44 *dst, const union matrix44 *m) +{ + float d = matrix44_determinant(m); + if (d == 0) + { + *dst = *m; + return dst; + } + d = 1.0f / d; + + dst->c[0][0] = d * (C[1][2] * C[2][3] * C[3][1] - C[1][3] * C[2][2] * C[3][1] + C[1][3] * C[2][1] * C[3][2] - C[1][1] * C[2][3] * C[3][2] - C[1][2] * C[2][1] * C[3][3] + C[1][1] * C[2][2] * C[3][3]); + dst->c[0][1] = d * (C[0][3] * C[2][2] * C[3][1] - C[0][2] * C[2][3] * C[3][1] - C[0][3] * C[2][1] * C[3][2] + C[0][1] * C[2][3] * C[3][2] + C[0][2] * C[2][1] * C[3][3] - C[0][1] * C[2][2] * C[3][3]); + dst->c[0][2] = d * (C[0][2] * C[1][3] * C[3][1] - C[0][3] * C[1][2] * C[3][1] + C[0][3] * C[1][1] * C[3][2] - C[0][1] * C[1][3] * C[3][2] - C[0][2] * C[1][1] * C[3][3] + C[0][1] * C[1][2] * C[3][3]); + dst->c[0][3] = d * (C[0][3] * C[1][2] * C[2][1] - C[0][2] * C[1][3] * C[2][1] - C[0][3] * C[1][1] * C[2][2] + C[0][1] * C[1][3] * C[2][2] + C[0][2] * C[1][1] * C[2][3] - C[0][1] * C[1][2] * C[2][3]); + dst->c[1][0] = d * (C[1][3] * C[2][2] * C[3][0] - C[1][2] * C[2][3] * C[3][0] - C[1][3] * C[2][0] * C[3][2] + C[1][0] * C[2][3] * C[3][2] + C[1][2] * C[2][0] * C[3][3] - C[1][0] * C[2][2] * C[3][3]); + dst->c[1][1] = d * (C[0][2] * C[2][3] * C[3][0] - C[0][3] * C[2][2] * C[3][0] + C[0][3] * C[2][0] * C[3][2] - C[0][0] * C[2][3] * C[3][2] - C[0][2] * C[2][0] * C[3][3] + C[0][0] * C[2][2] * C[3][3]); + dst->c[1][2] = d * (C[0][3] * C[1][2] * C[3][0] - C[0][2] * C[1][3] * C[3][0] - C[0][3] * C[1][0] * C[3][2] + C[0][0] * C[1][3] * C[3][2] + C[0][2] * C[1][0] * C[3][3] - C[0][0] * C[1][2] * C[3][3]); + dst->c[1][3] = d * (C[0][2] * C[1][3] * C[2][0] - C[0][3] * C[1][2] * C[2][0] + C[0][3] * C[1][0] * C[2][2] - C[0][0] * C[1][3] * C[2][2] - C[0][2] * C[1][0] * C[2][3] + C[0][0] * C[1][2] * C[2][3]); + dst->c[2][0] = d * (C[1][1] * C[2][3] * C[3][0] - C[1][3] * C[2][1] * C[3][0] + C[1][3] * C[2][0] * C[3][1] - C[1][0] * C[2][3] * C[3][1] - C[1][1] * C[2][0] * C[3][3] + C[1][0] * C[2][1] * C[3][3]); + dst->c[2][1] = d * (C[0][3] * C[2][1] * C[3][0] - C[0][1] * C[2][3] * C[3][0] - C[0][3] * C[2][0] * C[3][1] + C[0][0] * C[2][3] * C[3][1] + C[0][1] * C[2][0] * C[3][3] - C[0][0] * C[2][1] * C[3][3]); + dst->c[2][2] = d * (C[0][1] * C[1][3] * C[3][0] - C[0][3] * C[1][1] * C[3][0] + C[0][3] * C[1][0] * C[3][1] - C[0][0] * C[1][3] * C[3][1] - C[0][1] * C[1][0] * C[3][3] + C[0][0] * C[1][1] * C[3][3]); + dst->c[2][3] = d * (C[0][3] * C[1][1] * C[2][0] - C[0][1] * C[1][3] * C[2][0] - C[0][3] * C[1][0] * C[2][1] + C[0][0] * C[1][3] * C[2][1] + C[0][1] * C[1][0] * C[2][3] - C[0][0] * C[1][1] * C[2][3]); + dst->c[3][0] = d * (C[1][2] * C[2][1] * C[3][0] - C[1][1] * C[2][2] * C[3][0] - C[1][2] * C[2][0] * C[3][1] + C[1][0] * C[2][2] * C[3][1] + C[1][1] * C[2][0] * C[3][2] - C[1][0] * C[2][1] * C[3][2]); + dst->c[3][1] = d * (C[0][1] * C[2][2] * C[3][0] - C[0][2] * C[2][1] * C[3][0] + C[0][2] * C[2][0] * C[3][1] - C[0][0] * C[2][2] * C[3][1] - C[0][1] * C[2][0] * C[3][2] + C[0][0] * C[2][1] * C[3][2]); + dst->c[3][2] = d * (C[0][2] * C[1][1] * C[3][0] - C[0][1] * C[1][2] * C[3][0] - C[0][2] * C[1][0] * C[3][1] + C[0][0] * C[1][2] * C[3][1] + C[0][1] * C[1][0] * C[3][2] - C[0][0] * C[1][1] * C[3][2]); + dst->c[3][3] = d * (C[0][1] * C[1][2] * C[2][0] - C[0][2] * C[1][1] * C[2][0] + C[0][2] * C[1][0] * C[2][1] - C[0][0] * C[1][2] * C[2][1] - C[0][1] * C[1][0] * C[2][2] + C[0][0] * C[1][1] * C[2][2]); + + return dst; +} + +static inline struct vector3 * +matrix44_gettrans(const union matrix44 *m, struct vector3 *trans) +{ + // Getting translation is trivial + trans->x = C[3][0]; + trans->y = C[3][1]; + trans->z = C[3][2]; + + return trans; +} + +static inline struct vector3 * +matrix44_getscale(const union matrix44 *m, struct vector3 *scale) +{ + // Scale is length of columns + scale->x = sqrtf(C[0][0] * C[0][0] + C[0][1] * C[0][1] + C[0][2] * C[0][2]); + scale->y = sqrtf(C[1][0] * C[1][0] + C[1][1] * C[1][1] + C[1][2] * C[1][2]); + scale->z = sqrtf(C[2][0] * C[2][0] + C[2][1] * C[2][1] + C[2][2] * C[2][2]); + + return scale; +} + +static inline void +matrix44_decompose(const union matrix44 *m, struct vector3 *trans, struct vector3 *rot, struct vector3 *scale) +{ + matrix44_gettrans(m, trans); + matrix44_getscale(m, scale); + + if (scale->x == 0 || scale->y == 0 || scale->z == 0) + { + rot->x = 0; + rot->y = 0; + rot->z = 0; + return; + } + + // Detect negative scale with determinant and flip one arbitrary axis + if (matrix44_determinant(m) < 0) + scale->x = -scale->x; + + // Combined rotation matrix YXZ + // + // Cos[y]*Cos[z]+Sin[x]*Sin[y]*Sin[z] Cos[z]*Sin[x]*Sin[y]-Cos[y]*Sin[z] Cos[x]*Sin[y] + // Cos[x]*Sin[z] Cos[x]*Cos[z] -Sin[x] + // -Cos[z]*Sin[y]+Cos[y]*Sin[x]*Sin[z] Cos[y]*Cos[z]*Sin[x]+Sin[y]*Sin[z] Cos[x]*Cos[y] + + rot->x = asinf(-C[2][1] / scale->z); + + // Special case: Cos[x] == 0 (when Sin[x] is +/-1) + float f = fabsf(C[2][1] / scale->z); + + if (f > 0.999f && f < 1.001f) + { + // Pin arbitrarily one of y or z to zero + // Mathematical equivalent of gimbal lock + rot->y = 0; + + // Now: Cos[x] = 0, Sin[x] = +/-1, Cos[y] = 1, Sin[y] = 0 + // => m[0][0] = Cos[z] and m[1][0] = Sin[z] + rot->z = atan2f(-C[1][0] / scale->y, C[0][0] / scale->x); + } + else + { + // Standard case + rot->y = atan2f(C[2][0] / scale->z, C[2][2] / scale->z); + rot->z = atan2f(C[0][1] / scale->x, C[1][1] / scale->y); + } +} + +static inline float * +matrix44_to33(const union matrix44 *m, float m33[9]) +{ + m33[0] = C[0][0]; + m33[1] = C[0][1]; + m33[2] = C[0][2]; + m33[3] = C[1][0]; + m33[4] = C[1][1]; + m33[5] = C[1][2]; + m33[6] = C[2][0]; + m33[7] = C[2][1]; + m33[8] = C[2][2]; + + return m33; +} + +#undef C + +// plane + +static inline struct plane * +plane_init(struct plane *p, const struct vector3 *normal, float d) +{ + p->normal = *normal; + // normalize + float invLen = 1.0f / vector3_length(normal); + p->normal.x *= invLen; + p->normal.y *= invLen; + p->normal.z *= invLen; + p->dist = d * invLen; + + return p; +} + +static inline struct plane * +plane_init_dot3(struct plane *p, const struct vector3 *v0, const struct vector3 *v1, const struct vector3 *v2) +{ + struct vector3 a, b; + vector3_vector(&a, v1, v0); + vector3_vector(&b, v2, v0); + + vector3_cross(&p->normal, &a, &b); + vector3_normalize(&p->normal); + p->dist = -vector3_dot(&p->normal, v0); + + return p; +} + +static inline float +plane_dist(const struct plane *p, const struct vector3 *v) +{ + float d = vector3_dot(&p->normal, v); + return d + p->dist; +} + +// Intersection + +static inline struct vector3 * +intersection_raytriangle(const struct vector3 *rayOrig, const struct vector3 *rayDir, + const struct vector3 *vert0, const struct vector3 *vert1, const struct vector3 *vert2, + struct vector3 *intsPoint) +{ + // Idea: Tomas Moeller and Ben Trumbore + // in Fast, Minimum Storage Ray/Triangle Intersection + + // Find vectors for two edges sharing vert0 + struct vector3 edge1, edge2; + vector3_vector(&edge1, vert1, vert0); + vector3_vector(&edge2, vert2, vert0); + + // Begin calculating determinant - also used to calculate U parameter + struct vector3 pvec; + vector3_cross(&pvec, rayDir, &edge2); + + // If determinant is near zero, ray lies in plane of triangle + float det = vector3_dot(&edge1, &pvec); + + // *** Culling branch *** + /*if( det < FLT_EPSILON ) + return NULL; + + // Calculate distance from vert0 to ray origin + struct vector3 tvec; + vector3_vector(&tvec, rayOrig, &vert0); + + // Calculate U parameter and test bounds + float u = vector3_dot(&tvec, &pvec); + if (u < 0 || u > det ) + return NULL; + + // Prepare to test V parameter + struct vector3 qvec; + vector3_cross(&qvec, &tvec, &edge1); + + // Calculate V parameter and test bounds + float v = vector3_dot(rayDir, &qvec); + if (v < 0 || u + v > det ) + return NULL; + + // Calculate t, scale parameters, ray intersects triangle + float t = vector3_dot(&edge2, &qvec ) / det;*/ + + // *** Non-culling branch *** + if (det > -FLT_EPSILON && det < FLT_EPSILON) + return 0; + float inv_det = 1.0f / det; + + // Calculate distance from vert0 to ray origin + struct vector3 tvec; + vector3_vector(&tvec, rayOrig, vert0); + + // Calculate U parameter and test bounds + float u = vector3_dot(&tvec, &pvec) * inv_det; + if (u < 0.0f || u > 1.0f) + return 0; + + // Prepare to test V parameter + struct vector3 qvec; + vector3_cross(&qvec, &tvec, &edge1); + + // Calculate V parameter and test bounds + float v = vector3_dot(rayDir, &qvec) * inv_det; + if (v < 0.0f || u + v > 1.0f) + return 0; + + // Calculate t, ray intersects triangle + float t = vector3_dot(&edge2, &qvec) * inv_det; + + // Calculate intersection point and test ray length and direction + intsPoint->x = rayOrig->x + rayDir->x * t; + intsPoint->y = rayOrig->y + rayDir->y * t; + intsPoint->z = rayOrig->z + rayDir->z * t; + + struct vector3 vec; + vector3_vector(&vec, intsPoint, rayOrig); + if (vector3_dot(&vec, rayDir) < 0 || vector3_length(&vec) > vector3_length(rayDir)) + return NULL; + + return intsPoint; +} + +static inline float +minf(float a, float b) +{ + return a < b ? a : b; +} + +static inline float +maxf(float a, float b) +{ + return a > b ? a : b; +} + +static inline int +intersection_rayAABB(const struct vector3 *rayOrig, const struct vector3 *rayDir, + const struct vector3 *mins, const struct vector3 *maxs) +{ + // SLAB based optimized ray/AABB intersection routine + // Idea taken from http://ompf.org/ray/ + + float l1 = (mins->x - rayOrig->x) / rayDir->x; + float l2 = (maxs->x - rayOrig->x) / rayDir->x; + float lmin = minf(l1, l2); + float lmax = maxf(l1, l2); + + l1 = (mins->y - rayOrig->y) / rayDir->y; + l2 = (maxs->y - rayOrig->y) / rayDir->y; + lmin = maxf(minf(l1, l2), lmin); + lmax = minf(maxf(l1, l2), lmax); + + l1 = (mins->z - rayOrig->z) / rayDir->z; + l2 = (maxs->z - rayOrig->z) / rayDir->z; + lmin = maxf(minf(l1, l2), lmin); + lmax = minf(maxf(l1, l2), lmax); + + if ((lmax >= 0.0f) & (lmax >= lmin)) + { + // Consider length + const struct vector3 rayDest = {rayOrig->x + rayDir->x, rayOrig->y + rayDir->y, rayOrig->z + rayDir->z}; + const struct vector3 rayMins = {minf(rayDest.x, rayOrig->x), minf(rayDest.y, rayOrig->y), minf(rayDest.z, rayOrig->z)}; + const struct vector3 rayMaxs = {maxf(rayDest.x, rayOrig->x), maxf(rayDest.y, rayOrig->y), maxf(rayDest.z, rayOrig->z)}; + return (rayMins.x < maxs->x) && (rayMaxs.x > mins->x) && + (rayMins.y < maxs->y) && (rayMaxs.y > mins->y) && + (rayMins.z < maxs->z) && (rayMaxs.z > mins->z); + } + else + { + return 0; + } +} + +static inline float +vector3_distAABB(const struct vector3 *pos, const struct vector3 *mins, const struct vector3 *maxs) +{ + struct vector3 center; + struct vector3 extent; + center.x = (mins->x + maxs->x) * 0.5f; + center.y = (mins->y + maxs->y) * 0.5f; + center.z = (mins->z + maxs->z) * 0.5f; + + extent.x = (maxs->x - mins->x) * 0.5f; + extent.y = (maxs->y - mins->y) * 0.5f; + extent.z = (maxs->z - mins->z) * 0.5f; + + struct vector3 nearestVec; + nearestVec.x = maxf(0, fabsf(pos->x - center.x) - extent.x); + nearestVec.y = maxf(0, fabsf(pos->y - center.y) - extent.y); + nearestVec.z = maxf(0, fabsf(pos->z - center.z) - extent.z); + + return vector3_length(&nearestVec); +} + +#endif diff --git a/framework/lualib-src/lua-zset/lua-skiplist.c b/framework/lualib-src/lua-zset/lua-skiplist.c index a861efa..80e0f76 100644 --- a/framework/lualib-src/lua-zset/lua-skiplist.c +++ b/framework/lualib-src/lua-zset/lua-skiplist.c @@ -6,14 +6,14 @@ #include #include -#include "lua.h" #include "lauxlib.h" +#include "lua.h" #include "skiplist.h" -static inline skiplist* +static inline skiplist * _to_skiplist(lua_State *L) { skiplist **sl = lua_touserdata(L, 1); - if(sl==NULL) { + if (sl == NULL) { luaL_error(L, "must be skiplist object"); } return *sl; @@ -23,10 +23,7 @@ static int _insert(lua_State *L) { skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); - luaL_checktype(L, 3, LUA_TSTRING); - size_t len; - const char* ptr = lua_tolstring(L, 3, &len); - slobj *obj = slCreateObj(ptr, len); + lua_Integer obj = luaL_checkinteger(L, 3); slInsert(sl, score, obj); return 0; } @@ -35,18 +32,16 @@ static int _delete(lua_State *L) { skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); - luaL_checktype(L, 3, LUA_TSTRING); - slobj obj; - obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); - lua_pushboolean(L, slDelete(sl, score, &obj)); + lua_Integer obj = luaL_checkinteger(L, 3); + lua_pushboolean(L, slDelete(sl, score, obj)); return 1; } static void -_delete_rank_cb(void* ud, slobj *obj) { - lua_State *L = (lua_State*)ud; +_delete_rank_cb(void *ud, int64_t obj) { + lua_State *L = (lua_State *)ud; lua_pushvalue(L, 4); - lua_pushlstring(L, obj->ptr, obj->length); + lua_pushinteger(L, obj); lua_call(L, 1, 0); } @@ -77,17 +72,14 @@ static int _get_rank(lua_State *L) { skiplist *sl = _to_skiplist(L); double score = luaL_checknumber(L, 2); - luaL_checktype(L, 3, LUA_TSTRING); - slobj obj; - obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); + lua_Integer obj = luaL_checkinteger(L, 3); - unsigned long rank = slGetRank(sl, score, &obj); - if(rank == 0) { + unsigned long rank = slGetRank(sl, score, obj); + if (rank == 0) { return 0; } lua_pushinteger(L, rank); - return 1; } @@ -97,7 +89,33 @@ _get_rank_range(lua_State *L) { unsigned long r1 = luaL_checkinteger(L, 2); unsigned long r2 = luaL_checkinteger(L, 3); int reverse, rangelen; - if(r1 <= r2) { + if (r1 <= r2) { + reverse = 0; + rangelen = r2 - r1 + 1; + } else { + reverse = 1; + rangelen = r1 - r2 + 1; + } + + skiplistNode *node = slGetNodeByRank(sl, r1); + lua_createtable(L, rangelen, 0); + int n = 0; + while (node && n < rangelen) { + n++; + lua_pushinteger(L, node->obj); + lua_rawseti(L, -2, n); + node = reverse ? node->backward : node->level[0].forward; + } + return 1; +} + +static int +_get_rank_score_range(lua_State *L) { + skiplist *sl = _to_skiplist(L); + unsigned long r1 = luaL_checkinteger(L, 2); + unsigned long r2 = luaL_checkinteger(L, 3); + int reverse, rangelen; + if (r1 <= r2) { reverse = 0; rangelen = r2 - r1 + 1; } else { @@ -105,16 +123,20 @@ _get_rank_range(lua_State *L) { rangelen = r1 - r2 + 1; } - skiplistNode* node = slGetNodeByRank(sl, r1); + skiplistNode *node = slGetNodeByRank(sl, r1); lua_createtable(L, rangelen, 0); int n = 0; - while(node && n < rangelen) { + while (node && n < rangelen) { n++; + lua_createtable(L, 2, 0); + lua_pushinteger(L, node->obj); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, node->score); + lua_rawseti(L, -2, 2); - lua_pushlstring(L, node->obj->ptr, node->obj->length); lua_rawseti(L, -2, n); - node = reverse? node->backward : node->level[0].forward; - } + node = reverse ? node->backward : node->level[0].forward; + } return 1; } @@ -123,10 +145,10 @@ _get_score_range(lua_State *L) { skiplist *sl = _to_skiplist(L); double s1 = luaL_checknumber(L, 2); double s2 = luaL_checknumber(L, 3); - int reverse; + int reverse; skiplistNode *node; - if(s1 <= s2) { + if (s1 <= s2) { reverse = 0; node = slFirstInRange(sl, s1, s2); } else { @@ -136,18 +158,20 @@ _get_score_range(lua_State *L) { lua_newtable(L); int n = 0; - while(node) { - if(reverse) { - if(node->score < s2) break; + while (node) { + if (reverse) { + if (node->score < s2) + break; } else { - if(node->score > s2) break; + if (node->score > s2) + break; } n++; - lua_pushlstring(L, node->obj->ptr, node->obj->length); + lua_pushinteger(L, node->obj); lua_rawseti(L, -2, n); - node = reverse? node->backward:node->level[0].forward; + node = reverse ? node->backward : node->level[0].forward; } return 1; } @@ -158,24 +182,17 @@ _get_member_by_rank(lua_State *L){ unsigned long r = luaL_checkinteger(L, 2); skiplistNode *node = slGetNodeByRank(sl, r); if (node) { - lua_pushlstring(L, node->obj->ptr, node->obj->length); + lua_pushinteger(L, node->obj); return 1; } return 0; } -static int -_dump(lua_State *L) { - skiplist *sl = _to_skiplist(L); - slDump(sl); - return 0; -} - static int _new(lua_State *L) { skiplist *psl = slCreate(); - skiplist **sl = (skiplist**) lua_newuserdata(L, sizeof(skiplist*)); + skiplist **sl = (skiplist **)lua_newuserdata(L, sizeof(skiplist *)); *sl = psl; lua_pushvalue(L, lua_upvalueindex(1)); lua_setmetatable(L, -2); @@ -185,29 +202,29 @@ _new(lua_State *L) { static int _release(lua_State *L) { skiplist *sl = _to_skiplist(L); - printf("collect sl:%p\n", sl); + //printf("collect sl:%p\n", sl); slFree(sl); return 0; } -int luaopen_skiplist_c(lua_State *L) { -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM > 501 +LUAMOD_API int +luaopen_skiplist_c(lua_State *L) { luaL_checkversion(L); -#endif luaL_Reg l[] = { - {"insert", _insert}, - {"delete", _delete}, - {"delete_by_rank", _delete_by_rank}, - - {"get_count", _get_count}, - {"get_rank", _get_rank}, - {"get_rank_range", _get_rank_range}, - {"get_score_range", _get_score_range}, - {"get_member_by_rank", _get_member_by_rank}, - - {"dump", _dump}, - {NULL, NULL} + { "insert", _insert }, + { "delete", _delete }, + { "delete_by_rank", _delete_by_rank }, + + { "get_count", _get_count }, + { "get_rank", _get_rank }, + { "get_rank_range", _get_rank_range }, + { "get_score_range", _get_score_range }, + { "get_member_by_rank", _get_member_by_rank}, + + { "get_rank_score_range", _get_rank_score_range }, + + { NULL, NULL } }; lua_createtable(L, 0, 2); diff --git a/framework/lualib-src/lua-zset/lua-skipset.c b/framework/lualib-src/lua-zset/lua-skipset.c new file mode 100644 index 0000000..42cf1c4 --- /dev/null +++ b/framework/lualib-src/lua-zset/lua-skipset.c @@ -0,0 +1,338 @@ +#define LUA_LIB +#include +#include +#include + +#include "lauxlib.h" +#include "lua.h" + +#define SKIPLIST_MAXLEVEL 32 +#define SKIPLIST_P 0.25 + +typedef struct skipsetNode { + int64_t obj; + struct skipsetNode *backward; + struct skiplistLevel { + struct skipsetNode *forward; + unsigned int span; + } level[]; +} skipsetNode; + +typedef struct skipset { + struct skipsetNode *header, *tail; + unsigned long length; + int level; +} skipset; + +static int slRandomLevel(void) { + int level = 1; + while ((random() & 0xffff) < (SKIPLIST_P * 0xffff)) + level += 1; + return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; +} + +static skipsetNode *slCreateNode(int level, int64_t obj) { + skipsetNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); + n->obj = obj; + return n; +} + +static int slInsert(skipset *sl, int64_t obj) { + skipsetNode *update[SKIPLIST_MAXLEVEL], *x; + unsigned int rank[SKIPLIST_MAXLEVEL]; + int i, level; + + x = sl->header; + for (i = sl->level - 1; i >= 0; i--) { + rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1]; + while (x->level[i].forward && x->level[i].forward->obj < obj) { + rank[i] += x->level[i].span; + x = x->level[i].forward; + } + update[i] = x; + } + x = x->level[0].forward; + if (x && x->obj == obj) + return 0; + + level = slRandomLevel(); + if (level > sl->level) { + for (i = sl->level; i < level; i++) { + rank[i] = 0; + update[i] = sl->header; + update[i]->level[i].span = sl->length; + } + sl->level = level; + } + x = slCreateNode(level, obj); + for (i = 0; i < level; i++) { + x->level[i].forward = update[i]->level[i].forward; + update[i]->level[i].forward = x; + + x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]); + update[i]->level[i].span = (rank[0] - rank[i]) + 1; + } + + for (i = level; i < sl->level; i++) { + update[i]->level[i].span++; + } + + x->backward = (update[0] == sl->header) ? NULL : update[0]; + if (x->level[0].forward) + x->level[0].forward->backward = x; + else + sl->tail = x; + sl->length++; + return 1; +} + +static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) { + int i; + for (i = 0; i < sl->level; i++) { + if (update[i]->level[i].forward == x) { + update[i]->level[i].span += x->level[i].span - 1; + update[i]->level[i].forward = x->level[i].forward; + } else { + update[i]->level[i].span -= 1; + } + } + if (x->level[0].forward) { + x->level[0].forward->backward = x->backward; + } else { + sl->tail = x->backward; + } + while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL) + sl->level--; + sl->length--; +} + +static void slFreeNode(skipsetNode *node) { + free(node); +} + +static int slDelete(skipset *sl, int64_t obj) { + skipsetNode *update[SKIPLIST_MAXLEVEL], *x; + int i; + + x = sl->header; + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && x->level[i].forward->obj < obj) + x = x->level[i].forward; + update[i] = x; + } + + x = x->level[0].forward; + if (x && (x->obj == obj)) { + slDeleteNode(sl, x, update); + slFreeNode(x); + return 1; + } else { + return 0; /* not found */ + } + return 0; /* not found */ +} + +static unsigned long slGetRank(skipset *sl, int64_t obj) { + skipsetNode *x; + unsigned long rank = 0; + int i; + + x = sl->header; + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && x->level[i].forward->obj <= obj) { + rank += x->level[i].span; + x = x->level[i].forward; + } + if (x->obj && (x->obj == obj)) { + return rank; + } + } + return 0; +} + +static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) { + if (rank == 0 || rank > sl->length) + return NULL; + + skipsetNode *x; + unsigned long traversed = 0; + int i; + + x = sl->header; + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && (traversed + x->level[i].span) <= rank) { + traversed += x->level[i].span; + x = x->level[i].forward; + } + if (traversed == rank) { + return x; + } + } + + return NULL; +} + +unsigned long slDeleteByRank(skipset *sl, unsigned int rank) { + skipsetNode *update[SKIPLIST_MAXLEVEL], *x; + unsigned long traversed = 0; + int i; + + x = sl->header; + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && (traversed + x->level[i].span) < rank) { + traversed += x->level[i].span; + x = x->level[i].forward; + } + update[i] = x; + } + + x = x->level[0].forward; + if (x) { + slDeleteNode(sl, x, update); + slFreeNode(x); + return 1; + } + return 0; +} + +static skipset *slCreate(void) { + int j; + skipset *sl; + + sl = malloc(sizeof(*sl)); + sl->level = 1; + sl->length = 0; + sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0); + for (j = 0; j < SKIPLIST_MAXLEVEL; j++) { + sl->header->level[j].forward = NULL; + sl->header->level[j].span = 0; + } + sl->header->backward = NULL; + sl->tail = NULL; + return sl; +} + +static void slFree(skipset *sl) { + skipsetNode *node = sl->header->level[0].forward, *next; + free(sl->header); + while (node) { + next = node->level[0].forward; + slFreeNode(node); + node = next; + } + free(sl); +} + +static inline skipset * +_to_skipset(lua_State *L, int idx) { + skipset **sl = lua_touserdata(L, idx); + if (sl == NULL) { + luaL_error(L, "must be skipset object"); + } + return *sl; +} + +static int +_insert(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + lua_Integer obj = luaL_checkinteger(L, 2); + lua_pushboolean(L, slInsert(sl, obj)); + return 1; +} + +static int +_delete(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + lua_Integer obj = luaL_checkinteger(L, 2); + lua_pushboolean(L, slDelete(sl, obj)); + return 1; +} + +static int +_get_count(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + lua_pushinteger(L, sl->length); + return 1; +} + +static int +_get_rank(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + lua_Integer obj = luaL_checkinteger(L, 2); + + unsigned long rank = slGetRank(sl, obj); + if (rank == 0) { + return 0; + } + + lua_pushinteger(L, rank); + return 1; +} + +static int +_get_by_rank(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + unsigned long r1 = luaL_checkinteger(L, 2); + + skipsetNode *node = slGetNodeByRank(sl, r1); + if (node) { + lua_pushinteger(L, node->obj); + return 1; + } + return 0; +} + +static int +_delete_by_rank(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + unsigned int rank = luaL_checkinteger(L, 2); + lua_pushinteger(L, slDeleteByRank(sl, rank)); + return 1; +} + +static int +_new(lua_State *L) { + skipset *psl = slCreate(); + + skipset **sl = (skipset **)lua_newuserdata(L, sizeof(skipset *)); + *sl = psl; + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + return 1; +} + +static int +_release(lua_State *L) { + skipset *sl = _to_skipset(L, 1); + slFree(sl); + return 0; +} + +LUAMOD_API int +luaopen_skipset_c(lua_State *L) { + luaL_checkversion(L); + + luaL_Reg l[] = { + { "insert", _insert }, + { "delete", _delete }, + { "delete_byrank", _delete_by_rank }, + + { "count", _get_count }, + { "rank", _get_rank }, + { "byrank", _get_by_rank }, + + { NULL, NULL } + }; + + lua_createtable(L, 0, 2); + + luaL_newlib(L, l); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, _release); + lua_setfield(L, -2, "__gc"); + lua_pushcfunction(L, _get_count); + lua_setfield(L, -2, "__len"); + + lua_pushcclosure(L, _new, 1); + return 1; +} diff --git a/framework/lualib-src/lua-zset/skiplist.c b/framework/lualib-src/lua-zset/skiplist.c index 2c662d0..ba468e7 100644 --- a/framework/lualib-src/lua-zset/skiplist.c +++ b/framework/lualib-src/lua-zset/skiplist.c @@ -4,21 +4,27 @@ */ // skiplist similar with the version in redis +#include #include #include #include -#include #include "skiplist.h" +#define SKIPLIST_MAXLEVEL 32 +#define SKIPLIST_P 0.25 -skiplistNode *slCreateNode(int level, double score, slobj *obj) { +skiplistNode *slCreateNode(int level, double score, int64_t obj) { skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); n->score = score; - n->obj = obj; + n->obj = obj; return n; } +void slFreeNode(skiplistNode *node) { + free(node); +} + skiplist *slCreate(void) { int j; skiplist *sl; @@ -26,8 +32,8 @@ skiplist *slCreate(void) { sl = malloc(sizeof(*sl)); sl->level = 1; sl->length = 0; - sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, NULL); - for (j=0; j < SKIPLIST_MAXLEVEL; j++) { + sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, 0); + for (j = 0; j < SKIPLIST_MAXLEVEL; j++) { sl->header->level[j].forward = NULL; sl->header->level[j].span = 0; } @@ -36,34 +42,10 @@ skiplist *slCreate(void) { return sl; } -slobj* slCreateObj(const char* ptr, size_t length) { - slobj *obj = malloc(sizeof(*obj)); - obj->ptr = malloc(length + 1); - - if(ptr) { - memcpy(obj->ptr, ptr, length); - } - obj->ptr[length] = '\0'; - - obj->length = length; - return obj; -} - -void slFreeObj(slobj *obj) { - free(obj->ptr); - free(obj); -} - -void slFreeNode(skiplistNode *node) { - slFreeObj(node->obj); - free(node); -} - void slFree(skiplist *sl) { skiplistNode *node = sl->header->level[0].forward, *next; - free(sl->header); - while(node) { + while (node) { next = node->level[0].forward; slFreeNode(node); node = next; @@ -73,34 +55,21 @@ void slFree(skiplist *sl) { int slRandomLevel(void) { int level = 1; - while((random() & 0xffff) < (SKIPLIST_P * 0xffff)) + while ((random() & 0xffff) < (SKIPLIST_P * 0xffff)) level += 1; return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; } -int compareslObj(slobj *a, slobj *b) { - int cmp = memcmp(a->ptr, b->ptr, a->length <= b->length ? a->length : b->length); - if(cmp == 0) return a->length - b->length; - return cmp; -} - -int equalslObj(slobj *a, slobj *b) { - return compareslObj(a, b) == 0; -} - -void slInsert(skiplist *sl, double score, slobj *obj) { +void slInsert(skiplist *sl, double score, int64_t obj) { skiplistNode *update[SKIPLIST_MAXLEVEL], *x; unsigned int rank[SKIPLIST_MAXLEVEL]; int i, level; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) { /* store rank that is crossed to reach the insert position */ - rank[i] = i == (sl->level-1) ? 0 : rank[i+1]; - while (x->level[i].forward && - (x->level[i].forward->score < score || - (x->level[i].forward->score == score && - compareslObj(x->level[i].forward->obj,obj) < 0))) { + rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1]; + while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) { rank[i] += x->level[i].span; x = x->level[i].forward; } @@ -119,7 +88,7 @@ void slInsert(skiplist *sl, double score, slobj *obj) { } sl->level = level; } - x = slCreateNode(level,score,obj); + x = slCreateNode(level, score, obj); for (i = 0; i < level; i++) { x->level[i].forward = update[i]->level[i].forward; update[i]->level[i].forward = x; @@ -158,29 +127,26 @@ void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) { } else { sl->tail = x->backward; } - while(sl->level > 1 && sl->header->level[sl->level-1].forward == NULL) + while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL) sl->level--; sl->length--; } /* Delete an element with matching score/object from the skiplist. */ -int slDelete(skiplist *sl, double score, slobj *obj) { +int slDelete(skiplist *sl, double score, int64_t obj) { skiplistNode *update[SKIPLIST_MAXLEVEL], *x; int i; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { - while (x->level[i].forward && - (x->level[i].forward->score < score || - (x->level[i].forward->score == score && - compareslObj(x->level[i].forward->obj,obj) < 0))) + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) x = x->level[i].forward; update[i] = x; } /* We may have multiple elements with the same score, what we need - * is to find the element with both the right score and object. */ + * is to find the element with both the right score and object. */ x = x->level[0].forward; - if (x && score == x->score && equalslObj(x->obj,obj)) { + if (x && score == x->score && (x->obj == obj)) { slDeleteNode(sl, x, update); slFreeNode(x); return 1; @@ -192,13 +158,13 @@ int slDelete(skiplist *sl, double score, slobj *obj) { /* Delete all the elements with rank between start and end from the skiplist. * Start and end are inclusive. Note that start and end need to be 1-based */ -unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud) { +unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud) { skiplistNode *update[SKIPLIST_MAXLEVEL], *x; unsigned long traversed = 0, removed = 0; int i; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) { while (x->level[i].forward && (traversed + x->level[i].span) < start) { traversed += x->level[i].span; x = x->level[i].forward; @@ -210,7 +176,7 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, x = x->level[0].forward; while (x && traversed <= end) { skiplistNode *next = x->level[0].forward; - slDeleteNode(sl,x,update); + slDeleteNode(sl, x, update); cb(ud, x->obj); slFreeNode(x); removed++; @@ -224,23 +190,20 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, * Returns 0 when the element cannot be found, rank otherwise. * Note that the rank is 1-based due to the span of sl->header to the * first element. */ -unsigned long slGetRank(skiplist *sl, double score, slobj *o) { +unsigned long slGetRank(skiplist *sl, double score, int64_t o) { skiplistNode *x; unsigned long rank = 0; int i; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { - while (x->level[i].forward && - (x->level[i].forward->score < score || - (x->level[i].forward->score == score && - compareslObj(x->level[i].forward->obj,o) <= 0))) { + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0))) { rank += x->level[i].span; x = x->level[i].forward; } /* x might be equal to sl->header, so test if obj is non-NULL */ - if (x->obj && equalslObj(x->obj, o)) { + if (x->obj && (x->obj == o)) { return rank; } } @@ -248,8 +211,8 @@ unsigned long slGetRank(skiplist *sl, double score, slobj *o) { } /* Finds an element by its rank. The rank argument needs to be 1-based. */ -skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) { - if(rank == 0 || rank > sl->length) { +skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) { + if (rank == 0 || rank > sl->length) { return NULL; } @@ -258,9 +221,8 @@ skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) { int i; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { - while (x->level[i].forward && (traversed + x->level[i].span) <= rank) - { + for (i = sl->level - 1; i >= 0; i--) { + while (x->level[i].forward && (traversed + x->level[i].span) <= rank) { traversed += x->level[i].span; x = x->level[i].forward; } @@ -278,7 +240,7 @@ int slIsInRange(skiplist *sl, double min, double max) { skiplistNode *x; /* Test for ranges that will always be empty. */ - if(min > max) { + if (min > max) { return 0; } x = sl->tail; @@ -298,13 +260,14 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { int i; /* If everything is out of range, return early. */ - if (!slIsInRange(sl,min, max)) return NULL; + if (!slIsInRange(sl, min, max)) + return NULL; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) { /* Go forward while *OUT* of range. */ while (x->level[i].forward && x->level[i].forward->score < min) - x = x->level[i].forward; + x = x->level[i].forward; } /* This is an inner range, so the next node cannot be NULL. */ @@ -319,29 +282,16 @@ skiplistNode *slLastInRange(skiplist *sl, double min, double max) { int i; /* If everything is out of range, return early. */ - if (!slIsInRange(sl, min, max)) return NULL; + if (!slIsInRange(sl, min, max)) + return NULL; x = sl->header; - for (i = sl->level-1; i >= 0; i--) { + for (i = sl->level - 1; i >= 0; i--) { /* Go forward while *IN* range. */ - while (x->level[i].forward && - x->level[i].forward->score <= max) - x = x->level[i].forward; + while (x->level[i].forward && x->level[i].forward->score <= max) + x = x->level[i].forward; } /* This is an inner range, so this node cannot be NULL. */ return x; } - -void slDump(skiplist *sl) { - skiplistNode *x; - int i; - - x = sl->header; - i = 0; - while(x->level[0].forward) { - x = x->level[0].forward; - i++; - printf("node %d: score:%f, member:%s\n", i, x->score, x->obj->ptr); - } -} diff --git a/framework/lualib-src/lua-zset/skiplist.h b/framework/lualib-src/lua-zset/skiplist.h index 5a7f8f8..d4c4dbd 100644 --- a/framework/lualib-src/lua-zset/skiplist.h +++ b/framework/lualib-src/lua-zset/skiplist.h @@ -1,22 +1,16 @@ -// -#include +#ifndef SKIPLIST_HH +#define SKIPLIST_HH -#define SKIPLIST_MAXLEVEL 32 -#define SKIPLIST_P 0.25 - -typedef struct slobj { - char *ptr; - size_t length; -} slobj; +#include typedef struct skiplistNode { - slobj* obj; + int64_t obj; double score; struct skiplistNode *backward; struct skiplistLevel { struct skiplistNode *forward; unsigned int span; - }level[]; + } level[]; } skiplistNode; typedef struct skiplist { @@ -25,20 +19,20 @@ typedef struct skiplist { int level; } skiplist; -typedef void (*slDeleteCb) (void *ud, slobj *obj); -slobj* slCreateObj(const char* ptr, size_t length); -void slFreeObj(slobj *obj); +typedef void (*slDeleteCb)(void *ud, int64_t obj); +void slFreeNode(skiplistNode *node); skiplist *slCreate(void); void slFree(skiplist *sl); -void slDump(skiplist *sl); -void slInsert(skiplist *sl, double score, slobj *obj); -int slDelete(skiplist *sl, double score, slobj *obj); -unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud); +void slInsert(skiplist *sl, double score, int64_t obj); +int slDelete(skiplist *sl, double score, int64_t obj); +unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud); -unsigned long slGetRank(skiplist *sl, double score, slobj *o); -skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank); +unsigned long slGetRank(skiplist *sl, double score, int64_t o); +skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank); skiplistNode *slFirstInRange(skiplist *sl, double min, double max); skiplistNode *slLastInRange(skiplist *sl, double min, double max); + +#endif //SKIPLIST_HH \ No newline at end of file diff --git a/framework/lualib-src/testluaclib.lua b/framework/lualib-src/testluaclib.lua index e57c1b3..90771cd 100644 --- a/framework/lualib-src/testluaclib.lua +++ b/framework/lualib-src/testluaclib.lua @@ -1,6 +1,9 @@ package.path = "3rd/?.lua;" .. package.path package.cpath = ";luaclib/?.so;skynet/luaclib/?.so;" +-- lua ffi 库 +-- https://github.com/q66/cffi-lua + local snapshot = require "snapshot" print("snapshot", snapshot) diff --git a/framework/lualib/3rd/mysqlauto/db.lua b/framework/lualib/3rd/mysqlauto/db.lua new file mode 100644 index 0000000..fa50715 --- /dev/null +++ b/framework/lualib/3rd/mysqlauto/db.lua @@ -0,0 +1,326 @@ +local _M = {} +local field_attrs = {"field", "type", "collation", "null", "key", "default", "extra", "comment", "the_index"} + +local function showcolumn(ctx, tbl) + local ret = {} + local sqlret = assert(ctx.query(string.format("show full columns from %s", tbl))) + for k, v in ipairs(sqlret) do + local c = {} + for _k, _v in pairs(v) do + c[string.lower(_k)] = _v + end + c.the_index = tostring(k) + for _, key in ipairs(field_attrs) do + if c[key] == nil then + c[key] = "__NULL__" + end + end + table.insert(ret, c) + end + return ret +end + +local function showtables(ctx) + local ret = {} + local sqlret = assert(ctx.query("show tables;")) + for _, v in ipairs(sqlret) do + for _, value in pairs(v) do + table.insert(ret, value) + end + end + return ret +end + +local function showcreatetable(ctx, tbl) + local sqlret = assert(ctx.query(string.format("show create table %s", tbl))) + local str = sqlret[1]["Create Table"] + str = string.gsub(str, " AUTO_INCREMENT=%d*", "") + str = string.gsub(str, " USING BTREE", "USING HASH") + str = string.gsub(str, " ROW_FORMAT=DYNAMIC", "") + str = string.gsub(str, " ROW_FORMAT=FIXED", "") + str = string.gsub(str, " ROW_FORMAT=COMPACT", "") + str = string.gsub(str, "ENGINE=%w*", "ENGINE=InnoDB") + return str +end + +local function showindex(ctx, tbl) + local ret = {} + local sqlret = assert(ctx.query(string.format("show index from %s", tbl))) + for _, v in ipairs(sqlret) do + local c = {} + for _k, _v in pairs(v) do + c[string.lower(_k)] = _v + end + table.insert(ret, c) + end + return ret +end + +local function showcreateprocedure(ctx, proc) + local sqlret = assert(ctx.query(string.format("show create procedure %s", proc))) + local str = sqlret[1]["Create Procedure"] + str = string.gsub(str, "CREATE(.*)PROCEDURE", "CREATE PROCEDURE") + -- str=string.format("DROP PROCEDURE IF EXISTS `%s`;\nDELIMITER $$\n%s\n$$\nDELIMITER ;",proc,str) + return str +end + +local function showcreatefunction(ctx, proc) + local sqlret = assert(ctx.query(string.format("show create function %s", proc))) + local str = sqlret[1]["Create Function"] + str = string.gsub(str, "CREATE(.*)FUNCTION", "CREATE FUNCTION") + -- str=string.format("DROP PROCEDURE IF EXISTS `%s`;\nDELIMITER $$\n%s\n$$\nDELIMITER ;",proc,str) + return str +end + +local function showdatabase(ctx) + local sqlret = assert(ctx.query("select database()")) + return sqlret[1]["database()"] +end + +local function showprocedures(ctx) + local dbname = showdatabase(ctx) + local sqlret = assert(ctx.query(string.format("select name,type from mysql.proc where db='%s'", dbname))) + local ret, func = {}, {} + for _, v in pairs(sqlret) do + local name, type = v.name, v.type + if type == 'PROCEDURE' then + table.insert(ret, name); + else + table.insert(func, name) + end + end + return ret, func +end + +function _M.newctx(ret, opt) + ret.query = assert(opt.query) + return ret +end + +function _M.load(ctx) + ctx.table_list = showtables(ctx) + ctx.table = setmetatable({}, { + __index = function(t, k) + local r = showcolumn(ctx, k) + t[k] = r + return r + end, + }) + ctx.tablecreate = setmetatable({}, { + __index = function(t, k) + local r = showcreatetable(ctx, k) + t[k] = r + return r + end, + }) + ctx.index = setmetatable({}, { + __index = function(t, k) + local r = showindex(ctx, k) + t[k] = r + return r + end, + }) + local proc, func = showprocedures(ctx) + ctx.proc_list = proc + ctx.proc = setmetatable({}, { + __index = function(t, k) + local r = showcreateprocedure(ctx, k) + t[k] = r + return r + end, + }) + ctx.func_list = func + ctx.func = setmetatable({}, { + __index = function(t, k) + local r = showcreatefunction(ctx, k) + t[k] = r + return r + end, + }) + return ctx +end + +local function array2dict(array) + local d = {} + for k, v in ipairs(array) do + d[v] = k + end + return d +end + +local function fieldfind(tbl, k) + for _, v in pairs(tbl) do + if v.field == k then + return v + end + end +end + +local function markfield(set, tbl, v) + local null = "" + local default = "" + if v.null == "NO" then + null = "NOT NULL" + end + if v.default == "__NULL__" then + if v.null ~= "NO" then + default = "DEFAULT NULL" + end + else + default = string.format("DEFAULT '%s'", v.default) + end + local collate = "" + if v.collation ~= "" and v.collation ~= "__NULL__" then + collate = string.format("COLLATE '%s'", v.collation) + end + local cmt = "" + if v.comment ~= "" then + cmt = string.format("COMMENT '%s'", v.comment) + end + local pos = 'FIRST' + if tonumber(v.the_index) > 1 then + pos = string.format("ALTER `%s`", set[v.the_index - 1].field) + end + return null, default, cmt, collate, pos +end + +local function make_changefield(set, tbl, v) + local null, default, cmt, collate, pos = markfield(set, tbl, v) + return string.format("alter table `%s` change column `%s` `%s` %s %s %s %s %s %s %s", tbl, v.field, v.field, + string.lower(v.type), null, default, cmt, collate, string.lower(v.extra or ""), pos) +end + +local function make_addfield(set, tbl, v) + local null, default, cmt, collate, pos = markfield(set, tbl, v) + return string.format("alter table `%s` add column `%s` %s %s %s %s %s %s %s", tbl, v.field, string.lower(v.type), + null, default, cmt, collate, string.lower(v.extra or ""), pos) +end + +local function tablefield_compare(l, r) + if l == r then + return true + end + assert(l.field == r.field) + for _, k in ipairs(field_attrs) do + if k ~= "key" and l[k] ~= r[k] then + return false + end + end + return true +end + +local function fields_re_index(tbl) + for k, v in ipairs(tbl) do + v.the_index = tostring(k) + end +end + +local function compare_fields(ret, name, lfields, rfields) + while true do + local over = true + for k, lfield in ipairs(lfields) do + local rfield = fieldfind(rfields, lfield.field) + if not rfield then + table.insert(ret, string.format("alter table `%s` drop column `%s`", name, lfield.field)) + table.remove(lfields, k) + over = false + break + end + end + fields_re_index(lfields) + if over then + break + end + end + + while true do + local over = true + for k, rfield in ipairs(rfields) do + local lfield = fieldfind(lfields, rfield.field) + if not lfield then + table.insert(ret, make_addfield(lfields, k, rfield)) + elseif not tablefield_compare(lfield, rfield) then + table.insert(ret, make_changefield(lfields, k, rfield)) + end + end + fields_re_index(lfields) + if over then + break + end + end +end + +local function gensql(left, right) + local sdict = array2dict(left.table_list) + local cdict = array2dict(right.table_list) + local ret = {} + for k in pairs(sdict) do + if not cdict[k] then + table.insert(ret, (string.format("drop table if exists `%s`", k))) + else + local stbl = left.table[k] + local ctbl = right.table[k] + compare_fields(ret, k, stbl, ctbl) + end + end + for k in pairs(cdict) do + if not sdict[k] then + table.insert(ret, right.tablecreate[k]) + end + end + + local sdict = array2dict(left.proc_list) + local cdict = array2dict(right.proc_list) + + for k in pairs(sdict) do + if not cdict[k] then + table.insert(ret, (string.format("drop procedure if exists `%s`", k))) + else + local sproc = left.proc[k] + local cproc = right.proc[k] + if sproc ~= cproc then + table.insert(ret, (string.format("drop procedure if exists `%s`", k))) + table.insert(ret, cproc) + end + end + end + for k in pairs(cdict) do + if not sdict[k] then + table.insert(ret, right.proc[k]) + end + end + + local sdict = array2dict(left.func_list) + local cdict = array2dict(right.func_list) + for k in pairs(sdict) do + if not cdict[k] then + table.insert(ret, (string.format("drop function if exists `%s`", k))) + else + local sfunc = left.func[k] + local cfunc = right.func[k] + if sfunc ~= cfunc then + table.insert(ret, (string.format("drop function if exists `%s`", k))) + table.insert(ret, cfunc) + end + end + end + for k in pairs(cdict) do + if not sdict[k] then + table.insert(ret, right.func[k]) + end + end + return ret +end + +function _M.save(ctx) + local left = _M.newctx({}, ctx) + _M.load(left) + + local ret = gensql(left, ctx) + + for _, v in ipairs(ret) do + left.query(v) + end +end + +return _M diff --git a/framework/lualib/3rd/mysqlauto/file.lua b/framework/lualib/3rd/mysqlauto/file.lua new file mode 100644 index 0000000..ef4bbe9 --- /dev/null +++ b/framework/lualib/3rd/mysqlauto/file.lua @@ -0,0 +1,162 @@ +local _M = {} + +local function getfile(ctx, f) + return string.format("%s/%s.%s", ctx.dir, ctx.name, f) +end + +local function writefile(name, str) + local file = assert(io.open(name, "w")) + file:write(str) + file:close() +end + +local function readfile(name) + local file = assert(io.open(name, "r")) + local str = file:read("*a") + file:close() + return str +end + +local function readfilelines(name) + local file = assert(io.open(name, "r")) + local ret = {} + while true do + local line = file:read("*l") + if not line then + break + end + table.insert(ret, line) + end + return ret +end + +local function encode_table_column(ret, attrs) + local function encode_column(lines) + local ret = {} + for _, k in ipairs(attrs) do + local v = lines[k] + if v then + table.insert(ret, string.format("%s='%s'", k, v)) + end + end + return (table.concat(ret, " ")) + end + local tbl = {} + for _, v in ipairs(ret) do + table.insert(tbl, encode_column(v)) + end + return (table.concat(tbl, "\n")) +end + +local function decode_table_column(tbls) + local function decode_column(linestr) + local ret = {} + for k, v in string.gmatch(linestr, "(%S+)='(.-)'") do + ret[k] = v + end + return ret + end + local ret = {} + for _, v in pairs(tbls) do + local r = decode_column(v) + table.insert(ret, r) + end + return ret +end + +local function decode_table_index(tbls) + local function decode_column(linestr) + local ret = {} + for k, v in string.gmatch(linestr, "(%S+)='(.-)'") do + ret[k] = v + end + return ret + end + local ret = {} + for _, v in pairs(tbls) do + local r = decode_column(v) + local old = ret[r.key_name] + if old then + old.column_name = old.column_name .. ',' .. r.column_name + else + ret[r.key_name] = r + end + end + return ret +end + +function _M.load(ctx) + ctx.table_list = readfilelines(getfile(ctx, "table.list")) + ctx.table = setmetatable({}, { + __index = function(t, k) + local str = readfilelines(getfile(ctx, "table." .. k)) + local r = decode_table_column(str) + t[k] = r + return r + end, + }) + ctx.tablecreate = setmetatable({}, { + __index = function(t, k) + local r = readfile(getfile(ctx, "tablecreate." .. k)) + t[k] = r + return r + end, + }) + ctx.index = setmetatable({}, { + __index = function(t, k) + local str = readfilelines(getfile(ctx, "index." .. k)) + local r = decode_table_index(str) + t[k] = r + return r + end, + }) + ctx.proc_list = readfilelines(getfile(ctx, "proc.list")) + ctx.proc = setmetatable({}, { + __index = function(t, k) + local r = readfile(getfile(ctx, "proc." .. k)) + t[k] = r + return r + end, + }) + ctx.func_list = readfilelines(getfile(ctx, "func.list")) + ctx.func = setmetatable({}, { + __index = function(t, k) + local r = readfile(getfile(ctx, "func." .. k)) + t[k] = r + return r + end, + }) + return ctx +end + +function _M.newctx(ret, opt) + ret.name = assert(opt.name) + ret.dir = assert(opt.dir) + return ret +end + +function _M.compare(ctx, new) + return new +end + +local index_field_attrs = {"table", "non_unique", "key_name", "seq_in_index", "column_name", "collation", "sub_part", + "packed", "null", "index_type", "the_index", "comment"} +local field_attrs = {"field", "type", "collation", "null", "key", "default", "extra", "comment", "the_index"} +function _M.save(ctx) + writefile(getfile(ctx, "table.list"), table.concat(ctx.table_list, "\n")) + for _, k in ipairs(ctx.table_list) do + writefile(getfile(ctx, "table." .. k), encode_table_column(ctx.table[k], field_attrs)) + writefile(getfile(ctx, "index." .. k), encode_table_column(ctx.index[k], index_field_attrs)) + writefile(getfile(ctx, "tablecreate." .. k), ctx.tablecreate[k]) + end + writefile(getfile(ctx, "proc.list"), table.concat(ctx.proc_list, "\n")) + for _, k in ipairs(ctx.proc_list) do + writefile(getfile(ctx, "proc." .. k), ctx.proc[k]) + end + writefile(getfile(ctx, "func.list"), table.concat(ctx.func_list, "\n")) + for _, k in ipairs(ctx.func_list) do + writefile(getfile(ctx, "func." .. k), ctx.func[k]) + end +end + +return _M diff --git a/framework/lualib/3rd/mysqlauto/mysqlauto.lua b/framework/lualib/3rd/mysqlauto/mysqlauto.lua new file mode 100644 index 0000000..8ab8cc1 --- /dev/null +++ b/framework/lualib/3rd/mysqlauto/mysqlauto.lua @@ -0,0 +1,28 @@ +-- mysql结构同步的lua库实现 + +local _M = {} + +_M.db = require "mysqlauto.db" +_M.file = require "mysqlauto.file" + +function _M.newctx(opt) + assert(opt.name) + assert(opt.query) + assert(opt.dir) + local ret = {} + _M.db.newctx(ret, opt) + _M.file.newctx(ret, opt) + return ret +end + +function _M.db2file(ctx) + _M.db.load(ctx) + _M.file.save(ctx) +end + +function _M.file2db(ctx) + _M.file.load(ctx) + _M.db.save(ctx) +end + +return _M diff --git a/framework/lualib/3rd/zeus/ecs.lua b/framework/lualib/3rd/zeus/ecs.lua index b8865ac..5e4ab3d 100644 --- a/framework/lualib/3rd/zeus/ecs.lua +++ b/framework/lualib/3rd/zeus/ecs.lua @@ -1,6 +1,13 @@ +-- https://github.com/cloudwu/luaecs + local ecs = require "ecs.core" local function get_attrib(opt, inout) + if opt == nil then + return { + exist = true, + } + end local desc = {} if opt == "?" then desc.opt = true @@ -14,6 +21,9 @@ local function get_attrib(opt, inout) elseif inout == "update" then desc.r = true desc.w = true + elseif inout == "exist" then + desc.exist = true + assert(not desc.opt) else assert(inout == "temp") end @@ -31,13 +41,19 @@ local function cache_world(obj, k) local typenames = c.typenames local desc = {} local idx = 1 - for key, opt, inout in pat:gmatch "([_%w]+)([:?])(%l+)" do + for token in pat:gmatch "[^ ]+" do + local key, padding = token:match "^([_%w]+)(.*)" + assert(key, "Invalid pattern") + local opt, inout + if padding ~= "" then + opt, inout = padding:match "^([:?])(%l+)$" + assert(opt, "Invalid pattern") + end local tc = assert(typenames[key]) local a = get_attrib(opt, inout) a.name = tc.name a.id = tc.id a.type = tc.type - local n = #tc for i = 1, #tc do a[i] = tc[i] end @@ -129,7 +145,8 @@ do -- newtype for i, v in ipairs(typeclass) do c[i] = align(c, parse(v)) end - if typeclass.type == "lua" then + local ttype = typeclass.type + if ttype == "lua" then assert(c.size == 0) c.size = ecs._LUAOBJECT c.islua = true @@ -142,7 +159,7 @@ do -- newtype c.pack = pack else -- size == 0, one value - if typeclass.type then + if ttype then local t = assert(typeid[typeclass.type]) c.type = t c.size = typesize[t] @@ -155,6 +172,26 @@ do -- newtype typenames[name] = c self:_newtype(id, c.size) end + + local _ref = ecs._ref + function ecs.ref(typeclass) + local c = { + size = 0, + } + for i, v in ipairs(typeclass) do + c[i] = align(c, parse(v)) + end + if c.size == 0 and typeclass.type then + if typeclass.type ~= "lua" then + local id = assert(typeid[typeclass.type]) + c[1] = align(c, {id, nil, 0}) + end + end + if c.size > 0 then + align_struct(c, c[1][1]) + end + return _ref(c) + end end local mapbool = { @@ -173,6 +210,7 @@ function M:new(obj) if tc.islua then self:_addcomponent(eid, tc.id, v) elseif tc.tag then + assert(tc.size == 0) self:_addcomponent(eid, tc.id) elseif tc.type then self:_addcomponent(eid, tc.id, string.pack(tc.pack, mapbool[v] or v)) @@ -203,4 +241,57 @@ function M:select(pat) return context[self].select[pat]() end +function M:sync(pat, iter) + local p = context[self].select[pat] + self:_sync(p, iter) +end + +function M:clear(name) + local id = assert(context[self].typenames[name].id) + self:_clear(id) +end + +function M:sort(sorted, name) + local ctx = context[self] + local typenames = ctx.typenames + local t = assert(typenames[name]) + assert(t.type == typeid.int or (#t == 1 and t[1][1] == typeid.float)) + local stype = typenames[sorted] + if stype == nil then + local id = ctx.id + 1 + assert(id <= ecs._MAXTYPE) + ctx.id = id + stype = { + id = id, + name = sorted, + size = ecs._ORDERKEY, + tag = true, + } + self:_newtype(id, stype.size) + typenames[sorted] = stype + else + assert(stype.size == ecs._ORDERKEY) + end + self:_sortkey(stype.id, t.id) +end + +do + local _singleton = M._singleton + function M:singleton(name, v) + local pat = context[self].select[name] + return _singleton(pat, v) + end +end + +function ecs.world() + local w = ecs._world() + context[w].typenames.REMOVED = { + name = "REMOVED", + id = ecs._REMOVED, + size = 0, + tag = true, + } + return w +end + return ecs diff --git a/framework/lualib/3rd/zeus/zset.lua b/framework/lualib/3rd/zeus/zset.lua index 65b0bb1..bbdee93 100644 --- a/framework/lualib/3rd/zeus/zset.lua +++ b/framework/lualib/3rd/zeus/zset.lua @@ -1,3 +1,8 @@ +-- https://github.com/hongling0/lua-zset + +-- local skipset = require "skipset.c" +-- local set = skipset() + local skiplist = require "skiplist.c" local mt = {} mt.__index = mt @@ -129,3 +134,4 @@ function M.new() return setmetatable(obj, mt) end return M + diff --git a/framework/lualib/zeus/skynet/gd.lua b/framework/lualib/zeus/skynet/gd.lua deleted file mode 100644 index e341df9..0000000 --- a/framework/lualib/zeus/skynet/gd.lua +++ /dev/null @@ -1,41 +0,0 @@ -local skynet = require "skynet" -local mc = require "skynet.multicast" -local st = require "skynet.sharetable" - -local shared -local channel - -local M - -local function init() - assert(not shared) - assert(not channel) - shared = skynet.uniqueservice "shared" - local name = skynet.call(shared, "lua", "channel") - channel = mc.new { - channel = name, - dispatch = function(_, _, filenames) - for _, filename in pairs(filenames) do - if M[filename] then - M[filename] = nil - end - end - end, - } -end - -M = setmetatable({}, { - __index = function(self, filename) - if not shared then - init() - end - - local obj = st.query(filename) - if obj then - self[filename] = obj - end - return obj - end, -}) - -return M diff --git a/framework/lualib/zeus/skynet/parallels.lua b/framework/lualib/zeus/skynet/parallels.lua index 50ad956..75c1fd6 100644 --- a/framework/lualib/zeus/skynet/parallels.lua +++ b/framework/lualib/zeus/skynet/parallels.lua @@ -68,7 +68,7 @@ function M:add(func, ...) end function M:wait() - assert(not self.boot_co, "already in wait %s", tostring(self.boot_co)) + assert(not self.boot_co, string.format("already in wait %s", tostring(self.boot_co))) self.boot_co = coroutine.running() if not next(self.list) then -- skynet.yield() 相当于 skynet.sleep(0) 交出当前服务对 CPU 的控制权 diff --git a/framework/service/dbmgr.lua b/framework/service/dbmgr.lua deleted file mode 100644 index 045511a..0000000 --- a/framework/service/dbmgr.lua +++ /dev/null @@ -1,78 +0,0 @@ -local skynet = require "skynet" -local queue = require "skynet.queue" -local util = require "store_util" -require "skynet.manager" - -local dbconf -local lock = queue() -local guid_generator_addrs = {} - -local CMD = {} - -function CMD.init(conf) - assert(not dbconf, "dbmgr has been initialized.") - dbconf = conf -- init is allowed only once - - local uidconf = dbconf.guid_generator - assert(uidconf) - for _, worker_id in pairs(uidconf.worker_ids) do - local addr = skynet.newservice("guid_generator", worker_id) - table.insert(guid_generator_addrs, addr) - end - - local redisconf = dbconf.redis - for dbkey, conf in pairs(redisconf) do - for index = 1, conf.service_num do - local addr = skynet.newservice("redisd", dbkey, index) - local ok = skynet.call(addr, "lua", "init", conf) - if not ok then - assert(false, ("redisd init failed. [dbkey] %s [id] %d"):format(dbkey, index)) - end - end - end - - local mysqlconf = dbconf.mysql - for dbkey, conf in pairs(mysqlconf) do - for index = 1, conf.service_num do - local addr = skynet.newservice("mysqld", dbkey, index) - local ok = skynet.call(addr, "lua", "init", conf) - if not ok then - assert(false, ("mysqld init failed. [dbkey] %s [id] %d"):format(dbkey, index)) - end - end - end - - return true -end - -function CMD.mysql_service_num(dbkey) - if not dbconf then return end - local mysqlconf = dbconf.mysql - if not mysqlconf then return end - local conf = mysqlconf[dbkey] - if not conf then return end - return conf.service_num -end - -function CMD.redis_service_num(dbkey) - if not dbconf then return end - local redisconf = dbconf.redis - if not redisconf then return end - local conf = redisconf[dbkey] - if not conf then return end - return conf.service_num -end - -function CMD.guid_generators() - return guid_generator_addrs -end - -skynet.start(function() - skynet.dispatch("lua", function(_, _, cmd, ...) - local f = CMD[cmd] - assert(f, cmd) - skynet.retpack(f(...)) - end) -end) - -skynet.register(".dbmgr") \ No newline at end of file diff --git a/framework/service/mysqld.lua b/framework/service/mysqld.lua deleted file mode 100644 index a5404a7..0000000 --- a/framework/service/mysqld.lua +++ /dev/null @@ -1,50 +0,0 @@ -local skynet = require "skynet" -local mysql = require "skynet.db.mysql" -local util = require "store_util" -require "skynet.manager" - -local traceback = debug.traceback - -local dbkey, index = ... -local db - -local CMD = {} - -local function success(ret) - if not ret or ret.err or ret.badresult then - return false - end - return true -end - -function CMD.init(conf) - db = mysql.connect(conf) - db:query("set names utf8mb4") - return true -end - -function CMD.exec_one(sql) - local ok, ret = xpcall(db.query, traceback, db, sql) - if not ok or not success(ret) then - assert(false, ("sql=[%s] ret=[%s]"):format(sql, util.encode(ret))) - return - end - return ret -end - -function CMD.exec(sqls) - for i = 1, #sqls do - local sql = sqls[i] - CMD.exec_one(sql) - end -end - -skynet.start(function() - skynet.dispatch("lua", function(_, _, cmd, ...) - local f = CMD[cmd] - assert(f, cmd) - skynet.retpack(f(...)) - end) -end) - -skynet.register(util.mysql_sname(dbkey, index)) diff --git a/framework/service/redisd.lua b/framework/service/redisd.lua deleted file mode 100644 index 995339d..0000000 --- a/framework/service/redisd.lua +++ /dev/null @@ -1,42 +0,0 @@ -local skynet = require "skynet" -local redis = require "skynet.db.redis" -local util = require "store_util" -require "skynet.manager" - -local traceback = debug.traceback -local tunpack = table.unpack -local tconcat = table.concat -local dbkey, index = ... -local db - -local CMD = {} - -function CMD.init(conf) - db = redis.connect(conf) - return true -end - -function CMD.exec_one(cmd, ...) - local ok, ret = xpcall(db[cmd], traceback, db, ...) - if not ok then - assert(false, ("cmd=[%s %s] ret=[%s]"):format(cmd, tconcat({...}, " "), ret)) - return - end - return ret -end - -function CMD.exec(cmds) - for _, cmd in pairs(cmds) do - xpcall(CMD.exec_one, traceback, tunpack(cmd)) - end -end - -skynet.start(function() - skynet.dispatch("lua", function(_, _, cmd, ...) - local f = CMD[cmd] - assert(f, cmd) - skynet.retpack(f(...)) - end) -end) - -skynet.register(util.redis_sname(dbkey, index))