rotable2.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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 "rotable2.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. int n;
  20. } rotable;
  21. static char const unique_address[ 1 ] = { 0 };
  22. static int reg_compare(void const* a, void const* b) {
  23. return strcmp( (char const*)a, ((const rotable_Reg_t*)b)->name );
  24. }
  25. static int rotable_push_rovalue(lua_State *L, const rotable_Reg_t* q) {
  26. switch (q->value.type)
  27. {
  28. case LUA_TFUNCTION:
  29. lua_pushcfunction( L, q->value.value.func );
  30. break;
  31. case LUA_TINTEGER:
  32. lua_pushinteger( L, q->value.value.intvalue );
  33. break;
  34. case LUA_TSTRING:
  35. lua_pushstring( L, q->value.value.strvalue );
  36. break;
  37. case LUA_TNUMBER:
  38. lua_pushnumber( L, q->value.value.numvalue );
  39. break;
  40. case LUA_TLIGHTUSERDATA:
  41. lua_pushlightuserdata(L, q->value.value.ptr);
  42. break;
  43. default:
  44. return 0;
  45. }
  46. return 1;
  47. }
  48. static rotable* check_rotable( lua_State* L, int idx, char const* func ) {
  49. rotable* t = (rotable*)lua_touserdata( L, idx );
  50. if( t ) {
  51. if( lua_getmetatable( L, idx ) ) {
  52. lua_pushlightuserdata( L, (void*)unique_address );
  53. lua_rawget( L, LUA_REGISTRYINDEX );
  54. if( !lua_rawequal( L, -1, -2 ) )
  55. t = 0;
  56. lua_pop( L, 2 );
  57. }
  58. }
  59. if( !t ) {
  60. char const* type = lua_typename( L, lua_type( L, idx ) );
  61. if( lua_type( L, idx ) == LUA_TLIGHTUSERDATA ) {
  62. type = "light userdata";
  63. } else if( lua_getmetatable( L, idx ) ) {
  64. lua_getfield( L, -1, "__name" );
  65. lua_replace( L, -2 ); /* we don't need the metatable anymore */
  66. if( lua_type( L, -1 ) == LUA_TSTRING )
  67. type = lua_tostring( L, -1 );
  68. }
  69. lua_pushfstring( L, "bad argument #%d to '%s' "
  70. "(rotable expected, got %s)", idx, func, type );
  71. lua_error( L );
  72. }
  73. return t;
  74. }
  75. static const rotable_Reg_t* find_key( const rotable_Reg_t* p, int n,
  76. char const* s ) {
  77. (void)n;
  78. if( s ) {
  79. for( ; p->name != NULL; ++p ) {
  80. if( 0 == reg_compare( s, p ) )
  81. return p;
  82. }
  83. }
  84. return 0;
  85. }
  86. static int rotable_func_index( lua_State* L ) {
  87. char const* s = lua_tostring( L, 2 );
  88. const rotable_Reg_t* p = (const rotable_Reg_t*)lua_touserdata( L, lua_upvalueindex( 1 ) );
  89. const rotable_Reg_t* p2 = p;
  90. int n = lua_tointeger( L, lua_upvalueindex( 2 ) );
  91. p = find_key( p, n, s );
  92. if( p ) {
  93. rotable_push_rovalue(L, p);
  94. }
  95. else {
  96. // 看看第一个方法是不是__index, 如果是的话, 调用之
  97. if (p2->name != NULL && !strcmp("__index", p2->name)) {
  98. lua_pushcfunction(L, p2->value.value.func);
  99. lua_pushvalue(L, 2);
  100. lua_call(L, 1, 1);
  101. return 1;
  102. }
  103. lua_pushnil( L );
  104. }
  105. return 1;
  106. }
  107. static int rotable_udata_index( lua_State* L ) {
  108. rotable* t = (rotable*)lua_touserdata( L, 1 );
  109. char const* s = lua_tostring( L, 2 );
  110. const rotable_Reg_t* p = 0;
  111. lua_getuservalue( L, 1 );
  112. p = (const rotable_Reg_t*)lua_touserdata( L, -1 );
  113. const rotable_Reg_t* p2 = p;
  114. p = find_key( p, t->n, s );
  115. if( p ) {
  116. if (rotable_push_rovalue(L, p)) {
  117. return 1;
  118. }
  119. return 0;
  120. }
  121. else {
  122. // 看看第一个方法是不是__index, 如果是的话, 调用之
  123. if (p2->name != NULL && !strcmp("__index", p2->name)) {
  124. lua_pushcfunction(L, p2->value.value.func);
  125. lua_pushvalue(L, 2);
  126. lua_call(L, 1, 1);
  127. return 1;
  128. }
  129. lua_pushnil( L );
  130. }
  131. return 1;
  132. }
  133. static int rotable_udata_len( lua_State* L ) {
  134. lua_pushinteger( L, 0 );
  135. return 1;
  136. }
  137. static int rotable_iter( lua_State* L ) {
  138. check_rotable( L, 1, "__pairs iterator" );
  139. char const* key;
  140. const rotable_Reg_t* q = 0;
  141. const rotable_Reg_t* p = 0;
  142. int isnil = lua_isnil(L, 2);
  143. lua_getuservalue( L, 1 );
  144. p = (const rotable_Reg_t*)lua_touserdata( L, -1 );
  145. if (isnil) {
  146. lua_pushstring(L, p->name);
  147. rotable_push_rovalue(L, p);
  148. return 2;
  149. }
  150. key = lua_tostring(L, 2);
  151. for( q = p; q->name != NULL; ++q ) {
  152. if( 0 == reg_compare( key, q ) ) {
  153. ++q;
  154. break;
  155. }
  156. }
  157. if (q == NULL || q->name == NULL) {
  158. return 0;
  159. }
  160. lua_pushstring( L, q->name );
  161. rotable_push_rovalue(L, q);
  162. return 2;
  163. }
  164. static int rotable_udata_pairs( lua_State* L ) {
  165. lua_pushcfunction( L, rotable_iter );
  166. lua_pushvalue( L, 1 );
  167. lua_pushnil( L );
  168. return 3;
  169. }
  170. /**
  171. * 与lua_newlib对应的函数, 用于生成一个库table,区别是lua_newlib生成普通table,这个函数生成rotable.
  172. */
  173. #include "lgc.h"
  174. ROTABLE_EXPORT void rotable2_newlib( lua_State* L, void const* v ) {
  175. const rotable_Reg_t* reg = (const rotable_Reg_t*)v;
  176. rotable* t = (rotable*)lua_newuserdata( L, sizeof( *t ) );
  177. luaC_fix(L, L->l_G->allgc);
  178. lua_pushlightuserdata( L, (void*)unique_address );
  179. lua_rawget( L, LUA_REGISTRYINDEX );
  180. if( !lua_istable( L, -1 ) ) {
  181. lua_pop( L, 1 );
  182. lua_createtable( L, 0, 5 );
  183. lua_pushcfunction( L, rotable_udata_index );
  184. lua_setfield( L, -2, "__index" );
  185. lua_pushcfunction( L, rotable_udata_len );
  186. lua_setfield( L, -2, "__len" );
  187. lua_pushcfunction( L, rotable_udata_pairs );
  188. lua_setfield( L, -2, "__pairs" );
  189. lua_pushboolean( L, 0 );
  190. lua_setfield( L, -2, "__metatable" );
  191. lua_pushliteral( L, "rotable" );
  192. lua_setfield( L, -2, "__name" );
  193. lua_pushlightuserdata( L, (void*)unique_address );
  194. lua_pushvalue( L, -2 );
  195. lua_rawset( L, LUA_REGISTRYINDEX );
  196. }
  197. lua_setmetatable( L, -2 );
  198. #if LUA_VERSION_NUM < 503
  199. t->p = reg;
  200. #else
  201. lua_pushlightuserdata( L, (void*)reg );
  202. lua_setuservalue( L, -2 );
  203. #endif
  204. }
  205. /**
  206. * 为自定义对象也生成rotable形式的元表, 这个形式比rotable_newlib需要更多内存,但起码是一个解决办法.
  207. */
  208. ROTABLE_EXPORT void rotable2_newidx( lua_State* L, void const* v ) {
  209. lua_pushlightuserdata( L, (void*)v);
  210. lua_pushcclosure( L, rotable_func_index, 1 );
  211. }