Как сериализовать метатаблицу в Lua? - PullRequest
0 голосов
/ 30 апреля 2020
local binser = require "binser"

local log, floor, ceil, min, random = math.log, math.floor, math.ceil, math.min, math.random

local makeNode = function(value,size)
    return {
        value=value, 
        next={}, 
        width={}, 
        size=size
    }
end

local End ={}
local NIL = makeNode(End,0)

local insert = function(self,value)
    local node, chain, stepsAtLevel = self.head, {}, {}
    for i=1, self.maxLevel do stepsAtLevel[i]=0 end
    for level = self.maxLevel, 1, -1 do
        while node.next[level] ~= NIL and node.next[level].value <= value do
            stepsAtLevel[level] = ( stepsAtLevel[level] or 0 ) + node.width[level]
            node = node.next[level]
            --print(level, stepsAtLevel[level],value)
        end
        chain[level]=node
    end

    local nodeLevel = min( self.maxLevel, - floor(log(random()) / log(2) ) )
    local newNode = makeNode( value,  nodeLevel)
    local steps, prevNode = 0
    for level= 1, nodeLevel do
        prevNode = chain[level]
        newNode.next[level] = prevNode.next[level]
        prevNode.next[level] = newNode
        newNode.width[level] = prevNode.width[level] - steps
        prevNode.width[level] = steps + 1
        steps = steps + stepsAtLevel[level]
    end
    for level = nodeLevel + 1, self.maxLevel do
        chain[level].width[level] = chain[level].width[level] +1
    end
    self.size = self.size + 1
end

 local first = function(self)
    return self.head.next[1].value
end


local tostring = function (self)
    local t = {}
    for k,v in self:ipairs() do table.insert(t,v) end
    return "( "..table.concat(t,", ").. " )"
end


local islMT = {
    __index = function(self,i)
        if type(i) ~= "number" then return end
        if i > self.size then return end
        local node = self.head

        for level=self.maxLevel, 1, -1 do
            while node.width[level] <= i do
                i = i - node.width[level]
                node = node.next[level]
            end
        end
        return node.value
    end,
    __tostring=tostring
}


local ipairs = function (self)
    local node, size = self.head.next[1] , self.size
    count = 0
    return function()
        value=node.value
        node = node.next[1]
        count = count+1
        return count <= size and count or nil, value 
    end
end

math.randomseed(os.time())

local size = expected_size or 16
if not expected_size then
    expected_size = 16
end

local maxLevel = floor( log(expected_size) / log(2) ) 
local head = makeNode("HEAD",maxLevel)
for i=1,maxLevel do 
    head.next[i] = NIL
    head.width[i] = 1
end


local insdel = setmetatable( {
    size = 0,
    head = head,
    maxLevel = maxLevel,
    insert = insert,
    tostring = tostring,
    ipairs=ipairs,
    }, islMT 
) 

insdel:insert('foo') 
print(insdel)

-- how to serialize metatable insdel? this fail
local ser = binser.serialize(insdel)
insdel = binser.deserialize(ser)

insdel:insert('bar') 
print(insdel)

Я пробовал бесчисленное множество модулей сериализации (змей, json, плутон), и ни один из них не работает с метатаблями, кто-нибудь знает, как сериализовать метатаблицы? Какой модуль использовать для сериализации / десериализации metatable и как?

Полный код включен, модуль binser от luarocks: https://luarocks.org/modules/bakpakin/binser

...