来看一下 ZIO 缓冲。
词法分析读一个一个的字符就是从它读的。
或者 umdump 时也是从它读字符(一个 char 字节)的。
缓冲区隔离了下层数据源的不同,对上层提供一致的读取接口。
相关的代码文件是 lzio.h 和 lzio.c 。
先看一下数据结构:
#ifndef ZBSIZE #define ZBSIZE 256 /* buffer size */ #endif struct zio { size_t n; /* bytes still unread */ const unsigned char* p; /* current position in buffer */ int (*filbuf)(ZIO* z); void* u; /* additional data */ const char *name; unsigned char buffer[ZBSIZE]; /* buffer */ };
buffer 是缓冲区,数据就存在它里面。
n 是目前缓冲区还有多少没有读取的数据。
p 是缓冲区当前指针位置。
filbuf 是缓冲区数据为空是用来填充数据的。
看下它的接口,三个 open 接口,分别对应不同的源数据类型。
zread 读取下 n 个字节。
以及用宏定义出来的接口。
zgetc 取缓冲区当前字符,调整未读字符个数,及当前位置指针,如果缓冲区中没有可用的字符,则填充。
zungetc 放回缓冲区字符,也就是回退缓冲区当前位置指针,调整未读字符个数。
看下实现。
内存缓冲
static int zmfilbuf (ZIO* z) { (void)z; /* to avoid warnings */ return EOZ; } ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name) { if (b==NULL) return NULL; z->n = size; z->p = (const unsigned char *)b; z->filbuf = zmfilbuf; z->u = NULL; z->name = name; return z; }
内存缓冲,就是把一个缓冲区当前指针指向内存块 b 。设置相关的参数。
缓冲区填直接返回结束。因为一个内存块读完了就结束了。不存在所谓的再次填充的问题。
字符串缓冲
ZIO* zsopen (ZIO* z, const char* s, const char *name) { if (s==NULL) return NULL; return zmopen(z, s, strlen(s), name); }
调用上面的内存缓冲实现。
文件缓冲
static int zffilbuf (ZIO* z) { size_t n; if (feof((FILE *)z->u)) return EOZ; n = fread(z->buffer, 1, ZBSIZE, (FILE *)z->u); if (n==0) return EOZ; z->n = n-1; z->p = z->buffer; return *(z->p++); } ZIO* zFopen (ZIO* z, FILE* f, const char *name) { if (f==NULL) return NULL; z->n = 0; z->p = z->buffer; z->filbuf = zffilbuf; z->u = f; z->name = name; return z; }
文件缓冲传入文件描述符,及文件名。
重点看下它的填充函数 zffilbuf。
从文件中读取 ZBSIZE 个字符放到 buffer 中。
如果文件结束,则返回 EOZ。
设置未读字符为实际从文件中读取的字符数。
例如,文件过小,或者是最后一次读取时,可能文件中不到 ZBSIZE 个字符。
fread 返回值就是实际读取的字符数。
读取后将返回第一个字符,同时调整未读字符数及当前的指针。这个返回值在 zgetc 中会用到。
从缓冲区读取 n 个字符
size_t zread (ZIO *z, void *b, size_t n) { while (n) { size_t m; if (z->n == 0) { if (z->filbuf(z) == EOZ) return n; /* return number of missing bytes */ zungetc(z); /* put result from `filbuf' in the buffer */ } 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; }
从缓冲区中读取 n 个字符。
如果缓冲区中没有字符可读,则调用填充函数。注意 zungetc 的使用,因为在填充函数最后,移动指针了。
读取缓冲区未读的和需要读入的字符个数的较小值。
拷贝到输出。
调整缓冲区未读的字符数,缓冲区当前指针及输出的当前指针,调整待读取字符串个数。
----------------------------------------
到目前为止的问题:
> 函数原型优化 luaU_optchunk
> 打印函数原型 luaU_printchunk
> dump 函数原型 luaU_dumpchunk
> 语法分析 luaY_parser
----------------------------------------