本系列不會講 Lua 的基礎(chǔ)語法,由于Lua的輕便簡潔,讀者自行搜索了解,很快就可以入門。
本節(jié)開始,將直接進(jìn)入 Lua 的 C API 探索,探索順序基本與 Lua 參考手冊一致。
因為是第一次讀 Lua 源碼,若有錯誤,尚請指教和見諒。
lua_absindex
解析
我們找到 lua_absindex 在源碼中的定義:
// lapi.c 160
/*
** convert an acceptable stack index into an absolute index
*/
LUA_API int lua_absindex (lua_State *L, int idx) {
return (idx > 0 || ispseudo(idx))
? idx
: cast_int(L->top - L->ci->func) + idx;
}
解釋下注釋:將一個可接受的索引 idx 轉(zhuǎn)換為絕對索引。
注意到實現(xiàn)中有一個函數(shù) ispseudo(idx),可以知道它用來檢驗索引是否處于可接受的范圍內(nèi)。
我們找到 ispseudo(idx) 的定義:
// lapi.c 46
#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
我靠,原來是一個宏定義,它的作用是將索引值 idx 跟 LUA_REGISTRYINDEX 做比較。
繼續(xù)查找 LUA_REGISTRYINDEX 的定義:
// lua.h 42
/*
** Pseudo-indices
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
** space after that to help overflow detection)
*/
#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
簡單解釋下注釋:-LUAI_MAXSTACK (注意是負(fù)值) 是棧最小的有效索引,同時 Lua 保留了一些空閑空間來檢測溢出。
我們接著來看 LUAI_MAXSTACK :
// luaconf.h 705
/*
@@ LUAI_MAXSTACK limits the size of the Lua stack.
** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack
** space (and to reserve some numbers for pseudo-indices).
*/
#if LUAI_BITSINT >= 32
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK 15000
#endif
注釋里頭說:LUAI_MAXSTACK 限制了 Lua 堆棧的大小,而 LUAI_MAXSTACK 的大小則由整型數(shù)值的字節(jié)大小決定。
好,假設(shè)我們是 64 位,那么 ispssudo 就可以重寫為:
#define ispseudo(i) ((i) <= (-1000000 - 1000))
現(xiàn)在我們知道,LUA_REGISTRYINDEX 定義為 堆棧最小的有效索引-1000,我們暫且稱之為注冊表索引。
回到 lua_absindex 上來,如果索引 idx 大于 0 或者小于注冊表索引,那么其絕對索引就原樣輸出;反之,則使用 cast_int(L->top - L->ci->func) + idx 進(jìn)行輸出。
找到 cast_int 的定義:
// llimits.h 111
#define cast(t, exp) ((t)(exp))
// llimits.h 116
#define cast_int(i) cast(int, (i))
很簡單,cast_int 就是將數(shù)據(jù)強制轉(zhuǎn)換為整型。
而 L->top - L->ci->func 計算的是棧頂元素索引與當(dāng)前調(diào)用函數(shù)在棧內(nèi)的索引之間的差值,根據(jù)這個差值,我們可以間接知道棧內(nèi)元素的個數(shù)。我們看獲得棧內(nèi)元素個數(shù)的函數(shù) lua_gettop 定義就知道了:
LUA_API int lua_gettop (lua_State *L) {
return cast_int(L->top - (L->ci->func + 1));
}
現(xiàn)在,簡短的幾行代碼都得到解釋了,我們可以據(jù)此得出一個結(jié)論:當(dāng)給定輸入?yún)?shù),也即索引時,lua_absindex 的輸出預(yù)期:
- 正數(shù):原樣輸出
- 超出注冊表索引:原樣輸出
- 在負(fù)數(shù)可接受索引范圍內(nèi):棧內(nèi)元素個數(shù)+1+索引
測試
- 測試用例
#include <lua.hpp>
#include <lualib.h>
#include <lauxlib.h>
void test_lua_api_absindex(int index)
{
lua_State *L = luaL_newstate();
// 壓入 index 個元素
for(int i=1; i<=index; i++)
{
lua_pushnumber(L, 1);
}
int positive = lua_absindex(L, 10);
int pseudo = lua_absindex(L, -1001000);
int negative = lua_absindex(L, -100);
printf("lab_absindex got result : \n postive = %d\npseudo = %d\nnegative = %d", positive, pseudo, negative);
}
int main(int argc, const char * argv[]) {
test_lua_api_absindex(1000);
return 0;
}
- 輸出
lab_absindex got result :
postive = 10
pseudo = -10001000
negative = 901 // 1000 + 1 - 100
Program ended with exit code: 0
結(jié)果符合我們的預(yù)期。