Lua 5.3.4 里的 lzio.c
lzio.c
主要有 3 个方法:
LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data)
LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n)
LUAI_FUNC int luaZ_fill (ZIO *z)
luaZ_init(lzio.c#38)
void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
z->L = L;
z->reader = reader;
z->data = data;
z->n = 0;
z->p = NULL;
}
luaZ_init 主要初始化一个 ZIO
结构体,而这个结构体从参数里面传进来。
typedef struct Zio ZIO;
struct Zio {
size_t n; /* bytes still unread */
const char *p; /* current position in buffer */
lua_Reader reader; /* reader function */
void *data; /* additional data */
lua_State *L; /* Lua state (for reader) */
};
LuaZ_init 只在 lapi.c
里的 lua_load
方法内被调用;而 lua_load
方法我暂时看到是在 lauxlib.c
里的 luaL_loadfilex
方法和 luaL_loadbufferx
方法中调用。这两个的不同在于,前者是在执行 lua xxx.lua
的时候调用读入一个文件;后者是在执行 lua -e stat
的时候读入这个字符串
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
const char *chunkname, const char *mode) {
ZIO z;
int status;
lua_lock(L);
if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname, mode);
if (status == LUA_OK) { /* no errors? */
LClosure *f = clLvalue(L->top - 1); /* get newly created function */
if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
Table *reg = hvalue(&G(L)->l_registry);
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt);
luaC_upvalbarrier(L, f->upvals[0]);
}
}
lua_unlock(L);
return status;
}
LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
const char *mode) {
LoadF lf;
int status, readstatus;
int c;
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
if (filename == NULL) {
lua_pushliteral(L, "=stdin");
lf.f = stdin;
}
else {
lua_pushfstring(L, "@%s", filename);
lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex);
}
if (skipcomment(&lf, &c)) /* read initial portion */
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
skipcomment(&lf, &c); /* re-read initial portion */
}
if (c != EOF)
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */
if (readstatus) {
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
return errfile(L, "read", fnameindex);
}
lua_remove(L, fnameindex);
return status;
}
LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
const char *name, const char *mode) {
LoadS ls;
ls.s = buff;
ls.size = size;
return lua_load(L, getS, &ls, name, mode);
}
很明显 luaL_loadfilex
和 luaL_loadbufferx
两个方法用不同的参数去调用 lua_load
。一个是 getF
作为 lua_Reader reader
,LoadF lf
作为 void *data
;另一个是 getS
作为 lua_Reader reader
,LoadS ls
作为 void *data
。它们的分别定义是这样的:
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L; /* not used */
if (lf->n > 0) { /* are there pre-read characters to be read? */
*size = lf->n; /* return them (chars already in buffer) */
lf->n = 0; /* no more pre-read characters */
}
else { /* read a block from file */
/* 'fread' can return > 0 *and* set the EOF flag. If next call to
'getF' called 'fread', it might still wait for user input.
The next check avoids this problem. */
if (feof(lf->f)) return NULL;
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
}
return lf->buff;
}
typedef struct LoadF {
int n; /* number of pre-read characters */
FILE *f; /* file being read */
char buff[BUFSIZ]; /* area for reading file */
} LoadF;
static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud;
(void)L; /* not used */
if (ls->size == 0) return NULL;
*size = ls->size;
ls->size = 0;
return ls->s;
}
typedef struct LoadS {
const char *s;
size_t size;
} LoadS;
这样,我们就可以看出来 lua_load
通过传入的参数调用 luaZ_init
,设置 ZIO z
的 L
, reader
(就是不同的 getF
和 getS
), data
(就是不同的 LoadF lf
和 LoadS ls
), n
(0), p
(NULL)。
luaZ_fill(lzio.c#23)
int luaZ_fill (ZIO *z) {
size_t size;
lua_State *L = z->L;
const char *buff;
lua_unlock(L);
buff = z->reader(L, z->data, &size);
lua_lock(L);
if (buff == NULL || size == 0)
return EOZ;
z->n = size - 1; /* discount char being returned */
z->p = buff;
return cast_uchar(*(z->p++));
}
这个方法主要通过 zgetc
这个宏被外界调用。
#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z))
zgetc
这儿宏会判断 Zio z
的 n
减一能否大于 0,也就是是否还有未处理的字符,如果有的话,就返回这个字符,并把 p
加一,否则的话,就执行 luaZ_fill(z)
,根据我们之前说到的 luaZ_init
,我们知道 n
是一开始就是 0 的,所以需要调用 luaZ_fill
。一开始我并不明白为什么这个方法叫 fill,我现在觉得是因为这个方法的职责就是把 reader 读到的数据装到 Zio z
里面,可以看到 buff = z->reader(L, z->data, &size);
这句将读到的数据记录到 buff 里面,最后设置 Zio z
的 n
为 size - 1
,p
为 buff
这个指针,最后会自增一,返回当前的第一个字符。
我不清楚大家会不会好奇在什么时候读入了数据,我是好奇的。对于执行命令 lua -e stat
的时候,字符串直接就在 argv 里面了;对于 lua -e xxx.lua
这样的命令,在 getF
里面调用了 fread
来读入文件的内容。
luaZ_read(lzio.c#48)
size_t luaZ_read (ZIO *z, void *b, size_t n) {
while (n) {
size_t m;
if (z->n == 0) { /* no bytes in buffer? */
if (luaZ_fill(z) == EOZ) /* try to read more */
return n; /* no more input; return number of missing bytes */
else {
z->n++; /* luaZ_fill consumed first byte; put it back */
z->p--;
}
}
m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
memcpy(b, z->p, m);
z->n -= m;
z->p += m;
b = (char *)b + m;
n -= m;
}
return 0;
}
这个方法会在执行 lua xxx.out
的时候被调用,其中 xxx.out
文件是通过 luac
生成的字节文件。这个方法通过调用 luaZ_fill(z)
来读入内容,不过在外面加入了循环来读入 n 个数据。