从 Lua 5.2 迁移到 5.3

jopen 5年前

在 2015 年的新年里, Lua 5.3 发布了 rc3 版

如果回顾 Lua 5.2 的发布历史,Lua 5.2 的 final 版是在 rc8 之后的 2011 年 12 月 17 日发布的,距离 rc1 的发布日 2011 年 11 月 24 日过去不到 1 个月。我们有理由相信正式版不远了。( 5.3 的 rc1 是 2014 年 12 月 17 日发布的)

这次升级对 Lua 语言层面的影响非常的小,但新增加的 int64 支持,以及 string pack 、utf8 库对开发帮助很大。所以我强烈建议正在使用 Lua 5.2 的项目尽快升级到 5.3 。相对而言,当初 5.1 向 5.2 升级的时候就痛苦的多(去掉了 setfenv ,增加了_ENV)。

我计划在 Lua 5.3 正式发布后,将 skynet 内置的 Lua 版本升级到 5.3 ,然后着手进行 skynet 1.0 的发布工作。

在 skynet 的应用环境下,我还是需要对 lua vm 的实现打一个 patch 让 不同的 lua vm 间可以共享 Proto 。但这个工作可以先不忙做,等正式发布后再来也可以。

目前可以先逐步升级 skynet 下的 lua 库。

我已经在 github 项目下创建了一个叫 lua53 的分支,做了一些工作。希望有同学可以帮忙一起 review 这部分代码。有兴趣的同学可以对照 最新的 commits 来检查这些升级做的变更。

必须做的修改是去掉 unsigned 有关的 api 调用。

lua 5.3 去掉了lua_pushunsignedlua_tounsigned等 api ,现在一律使用lua_pushinteger等。这些 api 默认操作lua_Integer这个数据类型。按文档的说法,在你的代码中,应该尽可能的使用lua_Integer。它默认等价于 long long ,至少保证 64 位字长(lua 5.3 可以配置成使用 32bit 整数,但在 skynet 的应用环境不会这么做)。如果需要无符号整数,可以再在 C 代码中做强制类型转换。

这部分工作做完后,整个代码就可以正确编译了。

但是,和序列化有关的库还需要为 lua 5.3 优化。因为 lua 5.3 原生支持了整型,不需要全部转换成 double 类型储存数字。

之前在做数据序列化工作时(seri 库 和 bson 库等),为了区分一个 number 类型到底是浮点数还是整数,我采取的方法是用lua_tonumber和lua_tointeger分别取一次,然后比较两个数值是否相等。在 lua 5.3 中,直接提供了更高效的lua_isinteger来做判断。

由于现在直接支持 64bit 整数,就不再需要使用 lightuserdata 来保存长整数了。所以我去掉了int64 库 。

相应的,相关的库应该做一些调整。pbc 库目前没有打包在 skynet 项目中,但我已经修改完毕,晚一点再放出来。skynet 内自带的序列化库,以及 bson ,redis 都需要做一些调整。

btw, 再修改序列化库时发现一个 bug ,再不支持非对齐地址访问的架构下会有点问题,这次一并修改了。

lua 5.3 不再提倡使用 bit32 库,而且这个库只对 32bit 整数有效,位操作现在提供了原生的操作符支持。(注:xor 是用 ~ 而不是 ^ ,因为 ^ 已经被用于 pow 操作了)我检索了整个代码,发现用到 bit32 最多的是那个从 openresty 移植来的 mysql driver 。

但实际上,在 lua 5.3 中不必再使用位操作去解析数据流了。因为有新的 string.pack 这个强大的 api 。比如:

local function _get_byte8(data, i)    local a, b, c, d, e, f, g, h = strbyte(data, i, i + 7)    -- XXX workaround for the lack of 64-bit support in bitop:    local lo = bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24))    local hi = bor(e, lshift(f, 8), lshift(g, 16), lshift(h, 24))    return lo + hi * 4294967296, i + 8  end

这个函数可以被简化成:

local function _get_byte8(data, i)      return strunpack("<I8",data,i)  end

在修改过程中,我发现 openresty 里这块代码写的很不 lua ,比如这个 dump 函数,

local function _dump(data)    local len = #data    local bytes = new_tab(len, 0)    for i = 1, len do      bytes[i] = format("%x", strbyte(data, i))    end    return concat(bytes, " ")  end

按 lua 的惯用法应该写成:

local function _dump(data)      return string.gsub(data, ".",           function(x) return format("%02x ", strbyte(x)) end)  end

这样既简洁,性能也好很多。

其实这是个普遍的问题。由于 Lua 天生是门嵌入语言,几乎所有的 Lua 程序员都用过别的语言。所以许多 Lua 程序员带着其他语言的经验来写。前段时间我就发现过另一个例子。

由于 mysql 这块改动最多,所以特别需要有人来一起 review 和测试。当然这块代码还有很多可以改进的地方,暂时就没有精力做了。如果有同学有兴趣,还可以把那块尚未完成的编码设置加进去。

原文  http://blog.codingnow.com/2015/01/lua_52_53.html