博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
lua序列化(支持循环引用)
阅读量:6469 次
发布时间:2019-06-23

本文共 4221 字,大约阅读时间需要 14 分钟。

lua序列化

  • 支持key类型为string, number
  • 支持value类型为string, number, table, boolean
  • 支持循环引用
  • 支持加密序列化
  • 支持loadstring反序列化

使用示例

local t = { a = 1, b = 2}local g = { c = 3, d = 4,  t}t.rt = glocal ser_str = ser(g)local unser_table = loadstring(ser_str)()

 

原理详解

采用递归序列化表的方式实现,并且支持循环引用。循环引用支持实现思路参考了云风的序列化.

先说不考虑支持循环引用的简单情况,采用递归方式实现序列化很简单

直接使用type函数分别判断表中key-value对儿分别是什么数据类型,然后分别处理

序列化key

local keystr = nilif type(k) == "string" then     keystr = string.format("[\"%s\"]", k)elseif type(k) == "number" then     keystr = string.format("[%d]", k)end

 

序列化value

 

local valuestr = nilif type(v) == "string" then     valuestr = string.format("\"%s\"", tostring(v))elseif type(v) == "number" or type(v) == "boolean" then      valuestr = tostring(v)elseif type(v) == "table" then    valuestr = table_ser(v)end

分别处理完key和value直接插入一个表容器中就可以,最后在使用table.concat连接字符串就可以,这里要说一下lua中拼接字符串是个比较低效的行为,这跟lua字符串实现有关,每拼接都会重新生成一个新串,所以字符串越长拼接会越慢

这里使用table.concat,不但减少了拼接次数,而且这样拼接效率比较高,因为是调用C拼接,而非Lua字符串的行为

在大字符串连接中,我们应避免..。应用table来模拟buffer,然后concat得到最终字符串

return string.format("{%s}", table.concat(container, ","))

 

精简后的代码

 

local function table_ser(tablevalue)    -- 记录表中各项    local container = {}    for k, v in pairs(tablevalue) do        -- 序列化key        local keystr = nil        if type(k) == "string" then             keystr = string.format("[\"%s\"]", k)        elseif type(k) == "number" then             keystr = string.format("[%d]", k)        end        -- 序列化value        local valuestr = nil        if type(v) == "string" then             valuestr = string.format("\"%s\"", tostring(v))        elseif type(v) == "number" or type(v) == "boolean" then             valuestr = tostring(v)        elseif type(v) == "table" then            valuestr = table_ser(v)        end        table.insert(container, string.format("%s=%s", keystr, valuestr))    end    return string.format("{%s}", table.concat(container, ","))end

支持循环引用

其实普通序列化没什么好说的,重点在于对循环引用的支持思路参考了云风的实现

使用一个表mark记录所有序列化过的表,并记录其全key(从根表到当前表的全路径key)每次新序列化一个表时,首先查看是否已经序列化过,若没有序列化则直接序列化, 若已经序列化过则处理如下:

在一个表assgin中记录所有循环引用情况,并给出正确赋值(因为循环引用不能直接序列化,
所以间接的在表构造之后赋值),最后可以一起拼接到一起。

table序列化实现如下:

 

local function table_ser(tablevalue, tablekey, mark, assign)    -- 标记当前table, 并记录其key名    mark[tablevalue] = tablekey    -- 记录表中各项    local container = {}    for k, v in pairs(tablevalue) do        -- 序列化key        local keystr = nil        if type(k) == "string" then             keystr = string.format("[\"%s\"]", k)        elseif type(k) == "number" then             keystr = string.format("[%d]", k)        end        -- 序列化value        local valuestr = nil        if type(v) == "string" then             valuestr = string.format("\"%s\"", tostring(v))        elseif type(v) == "number" or type(v) == "boolean" then             valuestr = tostring(v)        elseif type(v) == "table" then            -- 获得从根表到当前表项的完整key, tablekey(代表tablevalue的key), mark[v]代表table v的key            local fullkey = string.format("%s%s", tablekey, keystr)            if mark[v] then table.insert(assign, string.format("%s=%s", fullkey, mark[v]))            else valuestr = table_ser(v, fullkey, mark, assign)            end        end        if keystr and valuestr then            local keyvaluestr = string.format("%s=%s", keystr, valuestr)            table.insert(container, keyvaluestr)        end    end    return string.format("{%s}", table.concat(container, ","))end

调用table的序列化

local function ser(var, enc)    assert(type(var)=="table")    -- 标记所有出现的table, 并记录其key, 用于处理循环引用    local mark = {}    -- 用于记录循环引用的赋值语句    local assign = {}    -- 序列化表, ret字符串必须与后面的loca ret=%s中的ret相同,因为这个ret可能也会组织到结果字符串中。    local ret = table_ser(var, "ret", mark, assign)    local ret = string.format("local ret=%s %s return ret", ret, table.concat(assign, ";"))    return (enc==nil or enc==true) and string.dump(loadstring(ret)) or retend

 

mark:处理循环引用最重要的就是mark表,它记录了已经序列化的表和其完整key路径

assgin: 记录循环引用的后期赋值语句,将这些语句拼接到表构造之外

序列化后就是一个lua的table创建并赋值的代码字符串,所以可以使用loadstring直接加载,加载后是一个chunk,可以当作函数运行就返回结果

序列化加密

string.dump(loadstring(ret))

这就是加密的代码,因为string.dump参数必须是function, 所以使用loadstring将字符串加载成chunk,然后在由string.dump导成字节码

其实就是使用了string.dump函数,它可以把function导成二进制字节码,使用它处理一下就可以把明文字符串转成字节码了

完整代码见我github中的

转载于:https://www.cnblogs.com/luweimy/p/4104642.html

你可能感兴趣的文章
mysql的数据类型int、bigint、smallint 和 tinyint取值范围
查看>>
移动铁通宽带上网设置教程
查看>>
Python算法(含源代码下载)
查看>>
利用Windows自带的Certutil查看文件MD5
查看>>
通过原生js添加div和css
查看>>
简单的导出表格和将表格下载到桌面上。
查看>>
查询指定名称的文件
查看>>
AJAX POST&跨域 解决方案 - CORS
查看>>
开篇,博客的申请理由
查看>>
Servlet 技术全总结 (已完成,不定期增加内容)
查看>>
[JSOI2008]星球大战starwar BZOJ1015
查看>>
centos 7 部署LDAP服务
查看>>
iOS项目分层
查看>>
IntelliJ IDEA 注册码
查看>>
String字符串的截取
查看>>
DynamoDB Local for Desktop Development
查看>>
用javascript验证哥德巴赫猜想
查看>>
Shell编程-环境变量配置文件
查看>>
[Unity3d]DrawCall优化手记
查看>>
Struts2和Spring MVC的区别
查看>>