Skip to content

Commit

Permalink
feat(rotable): add rotable
Browse files Browse the repository at this point in the history
  • Loading branch information
XuNeo committed Jul 21, 2024
1 parent 4b7b3bf commit f9a10f0
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/luavgl.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "obj.c"
#include "timer.c"
#include "util.c"
#include "rotable.c"

static const struct luaL_Reg luavgl_methods[] = {
{"Timer", luavgl_timer_create}, /* timer.c */
Expand Down
243 changes: 243 additions & 0 deletions src/rotable.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/**
* rotable (c) 2017 Philipp Janda
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <lua.h>
#include "rotable.h"


/* The lookup code uses binary search on sorted `rotable_Reg` arrays
* to find functions/methods. For a small number of elements a linear
* search might be faster. */
#ifndef ROTABLE_BINSEARCH_MIN
# define ROTABLE_BINSEARCH_MIN 5
#endif


typedef struct {
#if LUA_VERSION_NUM < 503
/* on Lua 5.3 we use a lightuserdata in the uservalue of the
* userdata to hold the pointer to the luaL_Reg structure. Older
* Lua versions have to store it in the userdata payload. */
rotable_Reg const* p;
#endif
/* number of elements in the luaL_Reg array *if* it is sorted by
* name. We can use binary search in this case. Otherwise we need
* linear searches anyway and we can scan for the final `{NULL,0}`
* entry. `n` is 0 in this case. */
int n;
} rotable;


static char const unique_address[ 1 ] = { 0 };


static int reg_compare(void const* a, void const* b) {
return strcmp( (char const*)a, ((rotable_Reg const*)b)->name );
}


static rotable* check_rotable( lua_State* L, int idx, char const* func ) {
rotable* t = (rotable*)lua_touserdata( L, idx );
if( t ) {
if( lua_getmetatable( L, idx ) ) {
lua_pushlightuserdata( L, (void*)unique_address );
lua_rawget( L, LUA_REGISTRYINDEX );
if( !lua_rawequal( L, -1, -2 ) )
t = 0;
lua_pop( L, 2 );
}
}
if( !t ) {
char const* type = lua_typename( L, lua_type( L, idx ) );
if( lua_type( L, idx ) == LUA_TLIGHTUSERDATA ) {
type = "light userdata";
} else if( lua_getmetatable( L, idx ) ) {
lua_getfield( L, -1, "__name" );
lua_replace( L, -2 ); /* we don't need the metatable anymore */
if( lua_type( L, -1 ) == LUA_TSTRING )
type = lua_tostring( L, -1 );
}
lua_pushfstring( L, "bad argument #%d to '%s' "
"(rotable expected, got %s)", idx, func, type );
lua_error( L );
}
return t;
}


static rotable_Reg const* find_key( rotable_Reg const* p, int n,
char const* s ) {
if( s ) {
if( n >= ROTABLE_BINSEARCH_MIN ) { /* binary search */
return (rotable_Reg const*)bsearch( s, p, n, sizeof( *p ), reg_compare );
} else { /* use linear scan */
for( ; p->func; ++p ) {
if( 0 == reg_compare( s, p ) )
return p;
}
}
}
return 0;
}


static int rotable_func_index( lua_State* L ) {
char const* s = lua_tostring( L, 2 );
rotable_Reg const* p = (rotable_Reg const*)lua_touserdata( L, lua_upvalueindex( 1 ) );
int n = lua_tointeger( L, lua_upvalueindex( 2 ) );
p = find_key( p, n, s );
if( p )
lua_pushcfunction( L, p->func );
else
lua_pushnil( L );
return 1;
}


static int rotable_udata_index( lua_State* L ) {
rotable* t = (rotable*)lua_touserdata( L, 1 );
char const* s = lua_tostring( L, 2 );
#if LUA_VERSION_NUM < 503
rotable_Reg const* p = t->p;
#else
rotable_Reg const* p = 0;
lua_getuservalue( L, 1 );
p = (rotable_Reg const*)lua_touserdata( L, -1 );
#endif
p = find_key( p, t->n, s );
if( p )
lua_pushcfunction( L, p->func );
else
lua_pushnil( L );
return 1;
}


static int rotable_udata_len( lua_State* L ) {
lua_pushinteger( L, 0 );
return 1;
}


static int rotable_iter( lua_State* L ) {
rotable* t = check_rotable( L, 1, "__pairs iterator" );
char const* s = lua_tostring( L, 2 );
rotable_Reg const* q = 0;
#if LUA_VERSION_NUM < 503
rotable_Reg const* p = t->p;
#else
rotable_Reg const* p = 0;
lua_getuservalue( L, 1 );
p = (rotable_Reg const*)lua_touserdata( L, -1 );
#endif
if( s ) {
if( t->n >= ROTABLE_BINSEARCH_MIN ) { /* binary search */
q = (rotable_Reg const*)bsearch( s, p, t->n, sizeof( *p ), reg_compare );
if( q )
++q;
else
q = p + t->n;
} else { /* use linear scan */
for( q = p; q->func; ++q ) {
if( 0 == reg_compare( s, q ) ) {
++q;
break;
}
}
}
} else
q = p;
if( q->func ) {
lua_pushstring( L, q->name );
lua_pushcfunction( L, q->func );
return 2;
}
return 0;
}


static int rotable_udata_pairs( lua_State* L ) {
lua_pushcfunction( L, rotable_iter );
lua_pushvalue( L, 1 );
lua_pushnil( L );
return 3;
}


ROTABLE_EXPORT void rotable_newlib( lua_State* L, void const* v ) {
rotable_Reg const* reg = (rotable_Reg const*)v;
rotable* t = (rotable*)lua_newuserdata( L, sizeof( *t ) );
lua_pushlightuserdata( L, (void*)unique_address );
lua_rawget( L, LUA_REGISTRYINDEX );
if( !lua_istable( L, -1 ) ) {
lua_pop( L, 1 );
lua_createtable( L, 0, 5 );
lua_pushcfunction( L, rotable_udata_index );
lua_setfield( L, -2, "__index" );
lua_pushcfunction( L, rotable_udata_len );
lua_setfield( L, -2, "__len" );
lua_pushcfunction( L, rotable_udata_pairs );
lua_setfield( L, -2, "__pairs" );
lua_pushboolean( L, 0 );
lua_setfield( L, -2, "__metatable" );
lua_pushliteral( L, "rotable" );
lua_setfield( L, -2, "__name" );
lua_pushlightuserdata( L, (void*)unique_address );
lua_pushvalue( L, -2 );
lua_rawset( L, LUA_REGISTRYINDEX );
}
lua_setmetatable( L, -2 );
#if LUA_VERSION_NUM < 503
t->p = reg;
#else
lua_pushlightuserdata( L, (void*)reg );
lua_setuservalue( L, -2 );
#endif
t->n = 0;
if( reg->func ) {
int i = 1;
for( ; reg[ i ].func; ++i ) {
if( strcmp( reg[ i-1 ].name, reg[ i ].name ) >= 0 )
return;
}
t->n = i;
}
}


ROTABLE_EXPORT void rotable_newidx( lua_State* L, void const* v ) {
rotable_Reg const* reg = (rotable_Reg const*)v;
int i = 0;
lua_pushlightuserdata( L, (void*)v);
for( ; reg[ i ].func; ++i ) {
if( strcmp( reg[ i-1 ].name, reg[ i ].name ) >= 0 ) {
i = 0;
break;
}
}
if( i >= ROTABLE_BINSEARCH_MIN ) {
lua_pushinteger( L, i );
lua_pushcclosure( L, rotable_func_index, 2 );
} else
lua_pushcclosure( L, rotable_func_index, 1 );
}
47 changes: 47 additions & 0 deletions src/rotable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* rotable (c) 2017 Philipp Janda
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef ROTABLE_H_
#define ROTABLE_H_

#include <lua.h>

/* exactly the same as luaL_Reg, but since we are on small embedded
* microcontrollers, we don't assume that you have `lauxlib.h`
* available in your build! */
typedef struct rotable_Reg {
char const* name;
lua_CFunction func;
} rotable_Reg;

#ifndef ROTABLE_EXPORT
# define ROTABLE_EXPORT extern
#endif

/* compatible with `luaL_newlib()`, and works with `luaL_Reg` *and*
* `rotable_Reg` arrays (in case you don't use `lauxlib.h`) */
ROTABLE_EXPORT void rotable_newlib( lua_State* L, void const* reg );

/* Since userdatas can not be used as `__index` meta methods directly
* this function creates a C closure that looks up keys in a given
* `rotable_Reg` array. */
ROTABLE_EXPORT void rotable_newidx( lua_State* L, void const* reg );

#endif /* ROTABLE_H_ */

0 comments on commit f9a10f0

Please sign in to comment.