🐳 chore(工具): 增加 lua c toolkit

develop
xiaojin 5 years ago
parent b7815395e8
commit 4863ddbca7

@ -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

@ -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.

@ -0,0 +1,22 @@
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#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;
}

@ -0,0 +1,54 @@
#include <lua.h>
#include <lauxlib.h>
#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;
}

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <lua.h>
#include <lauxlib.h>
#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;
}

@ -0,0 +1,23 @@
#include <stdlib.h>
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#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;
}

@ -0,0 +1,8 @@
#ifndef PLUGIN_H_
#define PLUGIN_H_
#define PLUGIN "plugin_main"
typedef int (*plugin)( void );
#endif /* PLUGIN_H_ */

@ -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;
}

@ -0,0 +1,23 @@
#include <lua.h>
#include <lauxlib.h>
#include <stddef.h>
#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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,237 @@
/* Copyright 2013-2016 Philipp Janda <siffiejoe@gmx.net>
*
* 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 <stddef.h>
#if defined(__cplusplus) && !defined(MOON_LUA_CPP)
extern "C"
{
#endif
#include <lua.h>
#include <lauxlib.h>
#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_ */

@ -0,0 +1,183 @@
/* Copyright 2013-2016 Philipp Janda <siffiejoe@gmx.net>
*
* 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 <stdio.h>
#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 <unistd.h>
/* check for minimum POSIX version that specifies dlopen etc. */
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || \
defined(HAVE_DLFCN_H)
#include <dlfcn.h>
/* 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 <link.h>
#endif /* Linux with dl_iterate_phdr() function */
#if defined(__FreeBSD__) || defined(__DragonFly__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#define MOON_DLFIX_DL_ITERATE_PHDR
#include <link.h>
#endif /* BSDs with dl_iterate_phdr() function */
#if !defined(MOON_DLFIX_FIND) && \
defined(MOON_DLFIX_DL_ITERATE_PHDR)
#include <string.h>
#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_ */

@ -0,0 +1,160 @@
/* Copyright 2013-2015 Philipp Janda <siffiejoe@gmx.net>
*
* 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

File diff suppressed because it is too large Load Diff

@ -10,8 +10,12 @@ struct ecs_capi
void *(*iter)(struct entity_world *w, int cid, int index); void *(*iter)(struct entity_world *w, int cid, int index);
void (*clear_type)(struct entity_world *w, int cid); void (*clear_type)(struct entity_world *w, int cid);
void *(*sibling)(struct entity_world *w, int cid, int index, int slibling_id); 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 *(*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); 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 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, cid);
check_id_(ctx, slibling_id); 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 static inline void
entity_remove(struct ecs_context *ctx, int cid, int index) entity_remove(struct ecs_context *ctx, int cid, int index)
{ {
check_id_(ctx, cid); 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 #endif

@ -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

@ -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

@ -0,0 +1,853 @@
#include "math3d-left.h"
#include <lua.h>
#include <lauxlib.h>
// 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;
}

@ -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 <math.h>
#include <float.h>
#include <stddef.h>
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 <stdio.h>
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

@ -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 <math.h>
#include <float.h>
#include <stddef.h>
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

@ -6,14 +6,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "lua.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lua.h"
#include "skiplist.h" #include "skiplist.h"
static inline skiplist* static inline skiplist *
_to_skiplist(lua_State *L) { _to_skiplist(lua_State *L) {
skiplist **sl = lua_touserdata(L, 1); skiplist **sl = lua_touserdata(L, 1);
if(sl==NULL) { if (sl == NULL) {
luaL_error(L, "must be skiplist object"); luaL_error(L, "must be skiplist object");
} }
return *sl; return *sl;
@ -23,10 +23,7 @@ static int
_insert(lua_State *L) { _insert(lua_State *L) {
skiplist *sl = _to_skiplist(L); skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2); double score = luaL_checknumber(L, 2);
luaL_checktype(L, 3, LUA_TSTRING); lua_Integer obj = luaL_checkinteger(L, 3);
size_t len;
const char* ptr = lua_tolstring(L, 3, &len);
slobj *obj = slCreateObj(ptr, len);
slInsert(sl, score, obj); slInsert(sl, score, obj);
return 0; return 0;
} }
@ -35,18 +32,16 @@ static int
_delete(lua_State *L) { _delete(lua_State *L) {
skiplist *sl = _to_skiplist(L); skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2); double score = luaL_checknumber(L, 2);
luaL_checktype(L, 3, LUA_TSTRING); lua_Integer obj = luaL_checkinteger(L, 3);
slobj obj; lua_pushboolean(L, slDelete(sl, score, obj));
obj.ptr = (char *)lua_tolstring(L, 3, &obj.length);
lua_pushboolean(L, slDelete(sl, score, &obj));
return 1; return 1;
} }
static void static void
_delete_rank_cb(void* ud, slobj *obj) { _delete_rank_cb(void *ud, int64_t obj) {
lua_State *L = (lua_State*)ud; lua_State *L = (lua_State *)ud;
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
lua_pushlstring(L, obj->ptr, obj->length); lua_pushinteger(L, obj);
lua_call(L, 1, 0); lua_call(L, 1, 0);
} }
@ -77,17 +72,14 @@ static int
_get_rank(lua_State *L) { _get_rank(lua_State *L) {
skiplist *sl = _to_skiplist(L); skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2); double score = luaL_checknumber(L, 2);
luaL_checktype(L, 3, LUA_TSTRING); lua_Integer obj = luaL_checkinteger(L, 3);
slobj obj;
obj.ptr = (char *)lua_tolstring(L, 3, &obj.length);
unsigned long rank = slGetRank(sl, score, &obj); unsigned long rank = slGetRank(sl, score, obj);
if(rank == 0) { if (rank == 0) {
return 0; return 0;
} }
lua_pushinteger(L, rank); lua_pushinteger(L, rank);
return 1; return 1;
} }
@ -97,7 +89,33 @@ _get_rank_range(lua_State *L) {
unsigned long r1 = luaL_checkinteger(L, 2); unsigned long r1 = luaL_checkinteger(L, 2);
unsigned long r2 = luaL_checkinteger(L, 3); unsigned long r2 = luaL_checkinteger(L, 3);
int reverse, rangelen; 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; reverse = 0;
rangelen = r2 - r1 + 1; rangelen = r2 - r1 + 1;
} else { } else {
@ -105,16 +123,20 @@ _get_rank_range(lua_State *L) {
rangelen = r1 - r2 + 1; rangelen = r1 - r2 + 1;
} }
skiplistNode* node = slGetNodeByRank(sl, r1); skiplistNode *node = slGetNodeByRank(sl, r1);
lua_createtable(L, rangelen, 0); lua_createtable(L, rangelen, 0);
int n = 0; int n = 0;
while(node && n < rangelen) { while (node && n < rangelen) {
n++; 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); lua_rawseti(L, -2, n);
node = reverse? node->backward : node->level[0].forward; node = reverse ? node->backward : node->level[0].forward;
} }
return 1; return 1;
} }
@ -123,10 +145,10 @@ _get_score_range(lua_State *L) {
skiplist *sl = _to_skiplist(L); skiplist *sl = _to_skiplist(L);
double s1 = luaL_checknumber(L, 2); double s1 = luaL_checknumber(L, 2);
double s2 = luaL_checknumber(L, 3); double s2 = luaL_checknumber(L, 3);
int reverse; int reverse;
skiplistNode *node; skiplistNode *node;
if(s1 <= s2) { if (s1 <= s2) {
reverse = 0; reverse = 0;
node = slFirstInRange(sl, s1, s2); node = slFirstInRange(sl, s1, s2);
} else { } else {
@ -136,18 +158,20 @@ _get_score_range(lua_State *L) {
lua_newtable(L); lua_newtable(L);
int n = 0; int n = 0;
while(node) { while (node) {
if(reverse) { if (reverse) {
if(node->score < s2) break; if (node->score < s2)
break;
} else { } else {
if(node->score > s2) break; if (node->score > s2)
break;
} }
n++; n++;
lua_pushlstring(L, node->obj->ptr, node->obj->length); lua_pushinteger(L, node->obj);
lua_rawseti(L, -2, n); lua_rawseti(L, -2, n);
node = reverse? node->backward:node->level[0].forward; node = reverse ? node->backward : node->level[0].forward;
} }
return 1; return 1;
} }
@ -158,24 +182,17 @@ _get_member_by_rank(lua_State *L){
unsigned long r = luaL_checkinteger(L, 2); unsigned long r = luaL_checkinteger(L, 2);
skiplistNode *node = slGetNodeByRank(sl, r); skiplistNode *node = slGetNodeByRank(sl, r);
if (node) { if (node) {
lua_pushlstring(L, node->obj->ptr, node->obj->length); lua_pushinteger(L, node->obj);
return 1; return 1;
} }
return 0; return 0;
} }
static int
_dump(lua_State *L) {
skiplist *sl = _to_skiplist(L);
slDump(sl);
return 0;
}
static int static int
_new(lua_State *L) { _new(lua_State *L) {
skiplist *psl = slCreate(); skiplist *psl = slCreate();
skiplist **sl = (skiplist**) lua_newuserdata(L, sizeof(skiplist*)); skiplist **sl = (skiplist **)lua_newuserdata(L, sizeof(skiplist *));
*sl = psl; *sl = psl;
lua_pushvalue(L, lua_upvalueindex(1)); lua_pushvalue(L, lua_upvalueindex(1));
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@ -185,29 +202,29 @@ _new(lua_State *L) {
static int static int
_release(lua_State *L) { _release(lua_State *L) {
skiplist *sl = _to_skiplist(L); skiplist *sl = _to_skiplist(L);
printf("collect sl:%p\n", sl); //printf("collect sl:%p\n", sl);
slFree(sl); slFree(sl);
return 0; return 0;
} }
int luaopen_skiplist_c(lua_State *L) { LUAMOD_API int
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM > 501 luaopen_skiplist_c(lua_State *L) {
luaL_checkversion(L); luaL_checkversion(L);
#endif
luaL_Reg l[] = { luaL_Reg l[] = {
{"insert", _insert}, { "insert", _insert },
{"delete", _delete}, { "delete", _delete },
{"delete_by_rank", _delete_by_rank}, { "delete_by_rank", _delete_by_rank },
{"get_count", _get_count}, { "get_count", _get_count },
{"get_rank", _get_rank}, { "get_rank", _get_rank },
{"get_rank_range", _get_rank_range}, { "get_rank_range", _get_rank_range },
{"get_score_range", _get_score_range}, { "get_score_range", _get_score_range },
{"get_member_by_rank", _get_member_by_rank}, { "get_member_by_rank", _get_member_by_rank},
{"dump", _dump}, { "get_rank_score_range", _get_rank_score_range },
{NULL, NULL}
{ NULL, NULL }
}; };
lua_createtable(L, 0, 2); lua_createtable(L, 0, 2);

@ -0,0 +1,338 @@
#define LUA_LIB
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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;
}

@ -4,21 +4,27 @@
*/ */
// skiplist similar with the version in redis // skiplist similar with the version in redis
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h>
#include "skiplist.h" #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)); skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel));
n->score = score; n->score = score;
n->obj = obj; n->obj = obj;
return n; return n;
} }
void slFreeNode(skiplistNode *node) {
free(node);
}
skiplist *slCreate(void) { skiplist *slCreate(void) {
int j; int j;
skiplist *sl; skiplist *sl;
@ -26,8 +32,8 @@ skiplist *slCreate(void) {
sl = malloc(sizeof(*sl)); sl = malloc(sizeof(*sl));
sl->level = 1; sl->level = 1;
sl->length = 0; sl->length = 0;
sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, NULL); sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, 0);
for (j=0; j < SKIPLIST_MAXLEVEL; j++) { for (j = 0; j < SKIPLIST_MAXLEVEL; j++) {
sl->header->level[j].forward = NULL; sl->header->level[j].forward = NULL;
sl->header->level[j].span = 0; sl->header->level[j].span = 0;
} }
@ -36,34 +42,10 @@ skiplist *slCreate(void) {
return sl; 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) { void slFree(skiplist *sl) {
skiplistNode *node = sl->header->level[0].forward, *next; skiplistNode *node = sl->header->level[0].forward, *next;
free(sl->header); free(sl->header);
while(node) { while (node) {
next = node->level[0].forward; next = node->level[0].forward;
slFreeNode(node); slFreeNode(node);
node = next; node = next;
@ -73,34 +55,21 @@ void slFree(skiplist *sl) {
int slRandomLevel(void) { int slRandomLevel(void) {
int level = 1; int level = 1;
while((random() & 0xffff) < (SKIPLIST_P * 0xffff)) while ((random() & 0xffff) < (SKIPLIST_P * 0xffff))
level += 1; level += 1;
return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
} }
int compareslObj(slobj *a, slobj *b) { void slInsert(skiplist *sl, double score, int64_t obj) {
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) {
skiplistNode *update[SKIPLIST_MAXLEVEL], *x; skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned int rank[SKIPLIST_MAXLEVEL]; unsigned int rank[SKIPLIST_MAXLEVEL];
int i, level; int i, level;
x = sl->header; 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 */ /* store rank that is crossed to reach the insert position */
rank[i] = i == (sl->level-1) ? 0 : rank[i+1]; rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1];
while (x->level[i].forward && 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->level[i].forward->score < score ||
(x->level[i].forward->score == score &&
compareslObj(x->level[i].forward->obj,obj) < 0))) {
rank[i] += x->level[i].span; rank[i] += x->level[i].span;
x = x->level[i].forward; x = x->level[i].forward;
} }
@ -119,7 +88,7 @@ void slInsert(skiplist *sl, double score, slobj *obj) {
} }
sl->level = level; sl->level = level;
} }
x = slCreateNode(level,score,obj); x = slCreateNode(level, score, obj);
for (i = 0; i < level; i++) { for (i = 0; i < level; i++) {
x->level[i].forward = update[i]->level[i].forward; x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x; update[i]->level[i].forward = x;
@ -158,29 +127,26 @@ void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) {
} else { } else {
sl->tail = x->backward; 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->level--;
sl->length--; sl->length--;
} }
/* Delete an element with matching score/object from the skiplist. */ /* 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; skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
int i; int i;
x = sl->header; x = sl->header;
for (i = sl->level-1; i >= 0; i--) { for (i = sl->level - 1; i >= 0; i--) {
while (x->level[i].forward && 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->level[i].forward->score < score ||
(x->level[i].forward->score == score &&
compareslObj(x->level[i].forward->obj,obj) < 0)))
x = x->level[i].forward; x = x->level[i].forward;
update[i] = x; update[i] = x;
} }
/* We may have multiple elements with the same score, what we need /* 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; 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); slDeleteNode(sl, x, update);
slFreeNode(x); slFreeNode(x);
return 1; 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. /* 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 */ * 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; skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned long traversed = 0, removed = 0; unsigned long traversed = 0, removed = 0;
int i; int i;
x = sl->header; 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) { while (x->level[i].forward && (traversed + x->level[i].span) < start) {
traversed += x->level[i].span; traversed += x->level[i].span;
x = x->level[i].forward; 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; x = x->level[0].forward;
while (x && traversed <= end) { while (x && traversed <= end) {
skiplistNode *next = x->level[0].forward; skiplistNode *next = x->level[0].forward;
slDeleteNode(sl,x,update); slDeleteNode(sl, x, update);
cb(ud, x->obj); cb(ud, x->obj);
slFreeNode(x); slFreeNode(x);
removed++; 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. * 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 * Note that the rank is 1-based due to the span of sl->header to the
* first element. */ * first element. */
unsigned long slGetRank(skiplist *sl, double score, slobj *o) { unsigned long slGetRank(skiplist *sl, double score, int64_t o) {
skiplistNode *x; skiplistNode *x;
unsigned long rank = 0; unsigned long rank = 0;
int i; int i;
x = sl->header; x = sl->header;
for (i = sl->level-1; i >= 0; i--) { for (i = sl->level - 1; i >= 0; i--) {
while (x->level[i].forward && while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0))) {
(x->level[i].forward->score < score ||
(x->level[i].forward->score == score &&
compareslObj(x->level[i].forward->obj,o) <= 0))) {
rank += x->level[i].span; rank += x->level[i].span;
x = x->level[i].forward; x = x->level[i].forward;
} }
/* x might be equal to sl->header, so test if obj is non-NULL */ /* 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; 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. */ /* Finds an element by its rank. The rank argument needs to be 1-based. */
skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) { skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) {
if(rank == 0 || rank > sl->length) { if (rank == 0 || rank > sl->length) {
return NULL; return NULL;
} }
@ -258,9 +221,8 @@ skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) {
int i; int i;
x = sl->header; 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) <= rank) while (x->level[i].forward && (traversed + x->level[i].span) <= rank) {
{
traversed += x->level[i].span; traversed += x->level[i].span;
x = x->level[i].forward; x = x->level[i].forward;
} }
@ -278,7 +240,7 @@ int slIsInRange(skiplist *sl, double min, double max) {
skiplistNode *x; skiplistNode *x;
/* Test for ranges that will always be empty. */ /* Test for ranges that will always be empty. */
if(min > max) { if (min > max) {
return 0; return 0;
} }
x = sl->tail; x = sl->tail;
@ -298,13 +260,14 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) {
int i; int i;
/* If everything is out of range, return early. */ /* 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; 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. */ /* Go forward while *OUT* of range. */
while (x->level[i].forward && x->level[i].forward->score < min) 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. */ /* 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; int i;
/* If everything is out of range, return early. */ /* 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; x = sl->header;
for (i = sl->level-1; i >= 0; i--) { for (i = sl->level - 1; i >= 0; i--) {
/* Go forward while *IN* range. */ /* Go forward while *IN* range. */
while (x->level[i].forward && while (x->level[i].forward && x->level[i].forward->score <= max)
x->level[i].forward->score <= max) x = x->level[i].forward;
x = x->level[i].forward;
} }
/* This is an inner range, so this node cannot be NULL. */ /* This is an inner range, so this node cannot be NULL. */
return x; 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);
}
}

@ -1,22 +1,16 @@
// #ifndef SKIPLIST_HH
#include <stdlib.h> #define SKIPLIST_HH
#define SKIPLIST_MAXLEVEL 32 #include <stdint.h>
#define SKIPLIST_P 0.25
typedef struct slobj {
char *ptr;
size_t length;
} slobj;
typedef struct skiplistNode { typedef struct skiplistNode {
slobj* obj; int64_t obj;
double score; double score;
struct skiplistNode *backward; struct skiplistNode *backward;
struct skiplistLevel { struct skiplistLevel {
struct skiplistNode *forward; struct skiplistNode *forward;
unsigned int span; unsigned int span;
}level[]; } level[];
} skiplistNode; } skiplistNode;
typedef struct skiplist { typedef struct skiplist {
@ -25,20 +19,20 @@ typedef struct skiplist {
int level; int level;
} skiplist; } skiplist;
typedef void (*slDeleteCb) (void *ud, slobj *obj); typedef void (*slDeleteCb)(void *ud, int64_t obj);
slobj* slCreateObj(const char* ptr, size_t length); void slFreeNode(skiplistNode *node);
void slFreeObj(slobj *obj);
skiplist *slCreate(void); skiplist *slCreate(void);
void slFree(skiplist *sl); void slFree(skiplist *sl);
void slDump(skiplist *sl);
void slInsert(skiplist *sl, double score, slobj *obj); void slInsert(skiplist *sl, double score, int64_t obj);
int slDelete(skiplist *sl, double score, slobj *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 slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud);
unsigned long slGetRank(skiplist *sl, double score, slobj *o); unsigned long slGetRank(skiplist *sl, double score, int64_t o);
skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank); skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank);
skiplistNode *slFirstInRange(skiplist *sl, double min, double max); skiplistNode *slFirstInRange(skiplist *sl, double min, double max);
skiplistNode *slLastInRange(skiplist *sl, double min, double max); skiplistNode *slLastInRange(skiplist *sl, double min, double max);
#endif //SKIPLIST_HH

@ -1,6 +1,9 @@
package.path = "3rd/?.lua;" .. package.path package.path = "3rd/?.lua;" .. package.path
package.cpath = ";luaclib/?.so;skynet/luaclib/?.so;" package.cpath = ";luaclib/?.so;skynet/luaclib/?.so;"
-- lua ffi 库
-- https://github.com/q66/cffi-lua
local snapshot = require "snapshot" local snapshot = require "snapshot"
print("snapshot", snapshot) print("snapshot", snapshot)

@ -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

@ -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

@ -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

@ -1,6 +1,13 @@
-- https://github.com/cloudwu/luaecs
local ecs = require "ecs.core" local ecs = require "ecs.core"
local function get_attrib(opt, inout) local function get_attrib(opt, inout)
if opt == nil then
return {
exist = true,
}
end
local desc = {} local desc = {}
if opt == "?" then if opt == "?" then
desc.opt = true desc.opt = true
@ -14,6 +21,9 @@ local function get_attrib(opt, inout)
elseif inout == "update" then elseif inout == "update" then
desc.r = true desc.r = true
desc.w = true desc.w = true
elseif inout == "exist" then
desc.exist = true
assert(not desc.opt)
else else
assert(inout == "temp") assert(inout == "temp")
end end
@ -31,13 +41,19 @@ local function cache_world(obj, k)
local typenames = c.typenames local typenames = c.typenames
local desc = {} local desc = {}
local idx = 1 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 tc = assert(typenames[key])
local a = get_attrib(opt, inout) local a = get_attrib(opt, inout)
a.name = tc.name a.name = tc.name
a.id = tc.id a.id = tc.id
a.type = tc.type a.type = tc.type
local n = #tc
for i = 1, #tc do for i = 1, #tc do
a[i] = tc[i] a[i] = tc[i]
end end
@ -129,7 +145,8 @@ do -- newtype
for i, v in ipairs(typeclass) do for i, v in ipairs(typeclass) do
c[i] = align(c, parse(v)) c[i] = align(c, parse(v))
end end
if typeclass.type == "lua" then local ttype = typeclass.type
if ttype == "lua" then
assert(c.size == 0) assert(c.size == 0)
c.size = ecs._LUAOBJECT c.size = ecs._LUAOBJECT
c.islua = true c.islua = true
@ -142,7 +159,7 @@ do -- newtype
c.pack = pack c.pack = pack
else else
-- size == 0, one value -- size == 0, one value
if typeclass.type then if ttype then
local t = assert(typeid[typeclass.type]) local t = assert(typeid[typeclass.type])
c.type = t c.type = t
c.size = typesize[t] c.size = typesize[t]
@ -155,6 +172,26 @@ do -- newtype
typenames[name] = c typenames[name] = c
self:_newtype(id, c.size) self:_newtype(id, c.size)
end 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 end
local mapbool = { local mapbool = {
@ -173,6 +210,7 @@ function M:new(obj)
if tc.islua then if tc.islua then
self:_addcomponent(eid, tc.id, v) self:_addcomponent(eid, tc.id, v)
elseif tc.tag then elseif tc.tag then
assert(tc.size == 0)
self:_addcomponent(eid, tc.id) self:_addcomponent(eid, tc.id)
elseif tc.type then elseif tc.type then
self:_addcomponent(eid, tc.id, string.pack(tc.pack, mapbool[v] or v)) 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]() return context[self].select[pat]()
end 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 return ecs

@ -1,3 +1,8 @@
-- https://github.com/hongling0/lua-zset
-- local skipset = require "skipset.c"
-- local set = skipset()
local skiplist = require "skiplist.c" local skiplist = require "skiplist.c"
local mt = {} local mt = {}
mt.__index = mt mt.__index = mt
@ -129,3 +134,4 @@ function M.new()
return setmetatable(obj, mt) return setmetatable(obj, mt)
end end
return M return M

@ -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

@ -68,7 +68,7 @@ function M:add(func, ...)
end end
function M:wait() 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() self.boot_co = coroutine.running()
if not next(self.list) then if not next(self.list) then
-- skynet.yield() 相当于 skynet.sleep(0) 交出当前服务对 CPU 的控制权 -- skynet.yield() 相当于 skynet.sleep(0) 交出当前服务对 CPU 的控制权

@ -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")

@ -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))

@ -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))
Loading…
Cancel
Save