buffer 作为一个连续的内存区域概念,在lua中就是string了。一是内容是内存连续的,二是可以存储任何东西。在作为二进制buffer概念去使用 string 的时候,一些地方就需要注意了。
首先,buffer 和字符串的区别在于,字符串是可见字符的,c风格的字符串后面会添加一个 \0 来作为结尾,所以在 c 向 lua传递一个 buffer 的时候,请使用 lua_pushlstring 而非 lua_pushstring, 函数声明见下:
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
而在 lua 向 c 传递buffer的时候,在c端接收的时候,请使用 lua_tolstring, 而非 lua_tostring 或 tolua_tostring(tolua中接口), 函数声明如下:
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def);
其次,在对buffer进行操作,更改内部内容的时候,因为 lua 是不支持直接操作内存的,所以可以传递给c端,有c端去更改内部内容,这样lua端的字符串内容就被改变了。
但是看过lua源码之后,发现字符串有缓存。因为在lua中字符串是不会被修改的,即没有对应的修改字符串的API。字符串的不可变性,导致lua内部基于此形成一个缓存优化,即在构造一个字符串时候,会根据对应的哈希值寻找内部缓存,如果没有则新建一个并扔到缓存中去。而上面用c直接修改字符串内存的问题在于,字符串的内存内容改变了,而其哈希值却没变,从而导致了问题,例如:
local s1 = string.rep('\0', 10)
local s2 = string.rep('\0', 10)
上述代码中,s1 和 s2 对应的哈希值是一样的,因此在创建 s2 的时候,发现缓存中有个一样的,就直接返回 s1了。假如我们在 s1 新建之后,用c端硬性的修改内容区域,那么接下来获取的s2实际上并不是全0的内容了。
诚然,可以在 c 端修改内容的时候,连带更改对应的哈希值,但是这样对于 buffer 频繁修改的物件来说,未免太多余了,而且损耗性能。只好作罢。