rotable.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * rotable 实现 lua下的只读table, 用于通常只用于库的注册.</p>
  3. * 从原理上说, 是声明一个userdata内存块, 然后通过元表,将其伪装成table.<p/>
  4. * 因为元表是共享的,而userdata内存块也很小(小于30字节),而且不会与方法的数量有关, 节省大量内存.
  5. *
  6. */
  7. #include <stddef.h>
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include "lua.h"
  11. #include "rotable.h"
  12. /* The lookup code uses binary search on sorted `rotable_Reg` arrays
  13. * to find functions/methods. For a small number of elements a linear
  14. * search might be faster. */
  15. #ifndef ROTABLE_BINSEARCH_MIN
  16. # define ROTABLE_BINSEARCH_MIN 5
  17. #endif
  18. typedef struct {
  19. #if LUA_VERSION_NUM < 503
  20. /* on Lua 5.3 we use a lightuserdata in the uservalue of the
  21. * userdata to hold the pointer to the luaL_Reg structure. Older
  22. * Lua versions have to store it in the userdata payload. */
  23. rotable_Reg const* p;
  24. #endif
  25. /* number of elements in the luaL_Reg array *if* it is sorted by
  26. * name. We can use binary search in this case. Otherwise we need
  27. * linear searches anyway and we can scan for the final `{NULL,0}`
  28. * entry. `n` is 0 in this case. */
  29. int n;
  30. } rotable;
  31. static char const unique_address[ 1 ] = { 0 };
  32. static int reg_compare(void const* a, void const* b) {
  33. return strcmp( (char const*)a, ((rotable_Reg const*)b)->name );
  34. }
  35. static rotable* check_rotable( lua_State* L, int idx, char const* func ) {
  36. rotable* t = (rotable*)lua_touserdata( L, idx );
  37. if( t ) {
  38. if( lua_getmetatable( L, idx ) ) {
  39. lua_pushlightuserdata( L, (void*)unique_address );
  40. lua_rawget( L, LUA_REGISTRYINDEX );
  41. if( !lua_rawequal( L, -1, -2 ) )
  42. t = 0;
  43. lua_pop( L, 2 );
  44. }
  45. }
  46. if( !t ) {
  47. char const* type = lua_typename( L, lua_type( L, idx ) );
  48. if( lua_type( L, idx ) == LUA_TLIGHTUSERDATA ) {
  49. type = "light userdata";
  50. } else if( lua_getmetatable( L, idx ) ) {
  51. lua_getfield( L, -1, "__name" );
  52. lua_replace( L, -2 ); /* we don't need the metatable anymore */
  53. if( lua_type( L, -1 ) == LUA_TSTRING )
  54. type = lua_tostring( L, -1 );
  55. }
  56. lua_pushfstring( L, "bad argument #%d to '%s' "
  57. "(rotable expected, got %s)", idx, func, type );
  58. lua_error( L );
  59. }
  60. return t;
  61. }
  62. static rotable_Reg const* find_key( rotable_Reg const* p, int n,
  63. char const* s ) {
  64. if( s ) {
  65. //if( n >= ROTABLE_BINSEARCH_MIN ) { /* binary search */
  66. // return (rotable_Reg const*)bsearch( s, p, n, sizeof( *p ), reg_compare );
  67. //} else { /* use linear scan */
  68. for( ; p->name != NULL; ++p ) {
  69. if( 0 == reg_compare( s, p ) )
  70. return p;
  71. }
  72. //}
  73. }
  74. return 0;
  75. }
  76. static int rotable_func_index( lua_State* L ) {
  77. char const* s = lua_tostring( L, 2 );
  78. rotable_Reg const* p = (rotable_Reg const*)lua_touserdata( L, lua_upvalueindex( 1 ) );
  79. int n = lua_tointeger( L, lua_upvalueindex( 2 ) );
  80. p = find_key( p, n, s );
  81. if( p ) {
  82. if (p->func)
  83. lua_pushcfunction( L, p->func );
  84. else
  85. lua_pushinteger(L, p->value);
  86. }
  87. else
  88. lua_pushnil( L );
  89. return 1;
  90. }
  91. static int rotable_udata_index( lua_State* L ) {
  92. rotable* t = (rotable*)lua_touserdata( L, 1 );
  93. char const* s = lua_tostring( L, 2 );
  94. #if LUA_VERSION_NUM < 503
  95. rotable_Reg const* p = t->p;
  96. #else
  97. rotable_Reg const* p = 0;
  98. lua_getuservalue( L, 1 );
  99. p = (rotable_Reg const*)lua_touserdata( L, -1 );
  100. #endif
  101. p = find_key( p, t->n, s );
  102. if( p ) {
  103. if (p->func)
  104. lua_pushcfunction( L, p->func );
  105. else
  106. lua_pushinteger(L, p->value);
  107. }
  108. else
  109. lua_pushnil( L );
  110. return 1;
  111. }
  112. static int rotable_udata_len( lua_State* L ) {
  113. lua_pushinteger( L, 0 );
  114. return 1;
  115. }
  116. static int rotable_iter( lua_State* L ) {
  117. rotable* t = check_rotable( L, 1, "__pairs iterator" );
  118. char const* s = lua_tostring( L, 2 );
  119. rotable_Reg const* q = 0;
  120. #if LUA_VERSION_NUM < 503
  121. rotable_Reg const* p = t->p;
  122. #else
  123. rotable_Reg const* p = 0;
  124. lua_getuservalue( L, 1 );
  125. p = (rotable_Reg const*)lua_touserdata( L, -1 );
  126. #endif
  127. if( s ) {
  128. // if( t->n >= ROTABLE_BINSEARCH_MIN ) { /* binary search */
  129. // q = (rotable_Reg const*)bsearch( s, p, t->n, sizeof( *p ), reg_compare );
  130. // if( q )
  131. // ++q;
  132. // else
  133. // q = p + t->n;
  134. // } else { /* use linear scan */
  135. for( q = p; q->name != NULL; ++q ) {
  136. if( 0 == reg_compare( s, q ) ) {
  137. ++q;
  138. break;
  139. }
  140. }
  141. // }
  142. } else
  143. q = p;
  144. if( q->func ) {
  145. lua_pushstring( L, q->name );
  146. lua_pushcfunction( L, q->func );
  147. return 2;
  148. }
  149. return 0;
  150. }
  151. static int rotable_udata_pairs( lua_State* L ) {
  152. lua_pushcfunction( L, rotable_iter );
  153. lua_pushvalue( L, 1 );
  154. lua_pushnil( L );
  155. return 3;
  156. }
  157. /**
  158. * 与lua_newlib对应的函数, 用于生成一个库table,区别是lua_newlib生成普通table,这个函数生成rotable.
  159. */
  160. ROTABLE_EXPORT void rotable_newlib( lua_State* L, void const* v ) {
  161. rotable_Reg const* reg = (rotable_Reg const*)v;
  162. rotable* t = (rotable*)lua_newuserdata( L, sizeof( *t ) );
  163. lua_pushlightuserdata( L, (void*)unique_address );
  164. lua_rawget( L, LUA_REGISTRYINDEX );
  165. if( !lua_istable( L, -1 ) ) {
  166. lua_pop( L, 1 );
  167. lua_createtable( L, 0, 5 );
  168. lua_pushcfunction( L, rotable_udata_index );
  169. lua_setfield( L, -2, "__index" );
  170. lua_pushcfunction( L, rotable_udata_len );
  171. lua_setfield( L, -2, "__len" );
  172. lua_pushcfunction( L, rotable_udata_pairs );
  173. lua_setfield( L, -2, "__pairs" );
  174. lua_pushboolean( L, 0 );
  175. lua_setfield( L, -2, "__metatable" );
  176. lua_pushliteral( L, "rotable" );
  177. lua_setfield( L, -2, "__name" );
  178. lua_pushlightuserdata( L, (void*)unique_address );
  179. lua_pushvalue( L, -2 );
  180. lua_rawset( L, LUA_REGISTRYINDEX );
  181. }
  182. lua_setmetatable( L, -2 );
  183. #if LUA_VERSION_NUM < 503
  184. t->p = reg;
  185. #else
  186. lua_pushlightuserdata( L, (void*)reg );
  187. lua_setuservalue( L, -2 );
  188. #endif
  189. }
  190. /**
  191. * 为自定义对象也生成rotable形式的元表, 这个形式比rotable_newlib需要更多内存,但起码是一个解决办法.
  192. */
  193. ROTABLE_EXPORT void rotable_newidx( lua_State* L, void const* v ) {
  194. lua_pushlightuserdata( L, (void*)v);
  195. lua_pushcclosure( L, rotable_func_index, 1 );
  196. }