minetest-mm/mods/techpack/safer_lua/data_struct.lua

364 lines
7.4 KiB
Lua

--[[
SaferLua [safer_lua]
====================
Copyright (C) 2017-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
data_struct.lua:
see https://github.com/joe7575/techpack/wiki/Data-Structures
]]--
safer_lua.DataStructHelp = [[
Data structures as a secure shell over the LUA table type.
see https://github.com/joe7575/techpack/wiki/Data-Structures
'Arrays' are lists of elements, which can be addressed
by means of an index:
a = Array(1,2,3,4) --> {1,2,3,4}
a.add(6) --> {1,2,3,4,6}
a.set(2, 8) --> {1,8,3,4,6}
a.insert(5,7) --> {1,8,3,4,7,6}
a.remove(3) --> {1,8,4,7,6}
a.insert(1, "hello") --> {"hello",1,8,4,7,6}
a.size() --> function returns 6
a.memsize() --> return returns 10
a.next() --> for loop iterator function
a.sort(reverse) --> sort the array elements in place
Unlike arrays, which are indexed by a range of numbers,
'stores' are indexed by keys:
s = Store("a",4,"b",5) --> {a = 4, b = 5}
s.set("val", 12) --> {a = 4, b = 5, val = 12}
s.get("val") --> returns 12
s.set(0, "hello") --> {a = 4, b = 5, val = 12, [0] = "hello"}
s.del("val") --> {[0] = "hello"}
s.size() --> function returns 4
s.memsize() --> function returns 8
s.next() --> for loop iterator function
s.keys(order) --> return an array with the keys
A 'set' is an unordered collection with no duplicate
elements.
s = Set("Tom", "Lucy")
--> {Tom = true, Lucy = true}
s.del("Tom") --> {Lucy = true}
s.add("Susi") --> {Lucy = true, Susi = true}
s.has("Susi") --> function returns `true`
s.has("Mike") --> function returns `false`
s.size() --> function returns 2
s.memsize() --> function returns 8
s.next() --> for loop iterator function
]]
local function var_count(v)
if type(v) == "number" then
return 1
elseif type(v) == "boolean" then
return 1
elseif v == nil then
return 0
elseif type(v) == "string" then
return #v
elseif type(v) == "table" then
return v.memsize()
else
return nil
end
end
function safer_lua.Store(...)
local new_t = {}
local mt = {}
local MemSize = 0
local Size = 0
local Data = {}
mt.__newindex = function(t, k, v) return end
mt.count = var_count
new_t.set = function(k,v)
if type(k) == "number" then
if rawget(Data, k) then -- has entry?
MemSize = MemSize - mt.count(rawget(Data, k))
else
Size = Size + 1
end
MemSize = MemSize + mt.count(v)
rawset(Data, k, v)
elseif type(k) == "string" then
if rawget(Data, k) then -- has entry?
MemSize = MemSize - mt.count(rawget(Data, k))
else
MemSize = MemSize + mt.count(k)
Size = Size + 1
end
MemSize = MemSize + mt.count(v)
rawset(Data, k, v)
end
end
new_t.get = function(k)
return rawget(Data, k)
end
new_t.del = function(k)
if rawget(Data, k) then -- has entry?
MemSize = MemSize - mt.count(k)
MemSize = MemSize - mt.count(rawget(Data, k))
rawset(Data, k, nil)
Size = Size - 1
end
end
new_t.memsize = function(t)
return MemSize
end
new_t.size = function(t)
return Size
end
new_t.next = function(t)
local n = nil
return function ()
n = next(Data, n)
if n then return n, Data[n] end
end
end
new_t.keys = function(order)
local keyset = {}
local n = 0
local size = 0
for k,v in pairs(Data) do
n = n + 1
keyset[n] = k
size = size + var_count(k)
end
if order == "up" then
table.sort(keyset, function(a,b) return a > b end)
elseif order == "down" then
table.sort(keyset)
end
local a = safer_lua.Array()
a.__load(size, keyset)
return a
end
new_t.__dump = function()
-- remove the not serializable meta data
return {Type = "Store", Size = Size, MemSize = MemSize, Data = Data}
end
new_t.__load = function(size, memsize, data)
Size = size
MemSize = memsize
Data = data
end
for idx = 1,select('#',...),2 do
local k,v = select(idx,...),select(idx+1,...)
new_t.set(k,v)
end
return setmetatable(new_t, mt)
end
function safer_lua.Array(...)
local new_t = {}
local mt = {}
local MemSize = 0
local Data = {}
mt.__newindex = function(t, k, v) return end
mt.count = var_count
for idx = 1,select('#',...) do
local v = select(idx,...)
local cnt = mt.count(v)
if cnt then
MemSize = MemSize + cnt
rawset(Data, idx, v)
end
end
new_t.add = function(v)
MemSize = MemSize + mt.count(v)
local i = #Data + 1
table.insert(Data, i, v)
end
new_t.set = function(i,v)
i = math.min(#Data, i)
MemSize = MemSize - mt.count(rawget(Data, i))
MemSize = MemSize + mt.count(v)
rawset(Data, i, v)
end
new_t.get = function(i)
return Data[i]
end
new_t.insert = function(i, v)
MemSize = MemSize + mt.count(v)
i = math.min(#Data, i)
table.insert(Data, i, v)
end
new_t.remove = function(i)
local v = table.remove(Data, i)
MemSize = MemSize - mt.count(v)
return v
end
new_t.sort = function(reverse)
if reverse then
table.sort(Data, function(a,b) return a > b end)
else
table.sort(Data)
end
end
new_t.memsize = function(t)
return MemSize
end
new_t.size = function(t)
return #Data
end
new_t.next = function(t)
local i = 0
local n = #Data
return function ()
i = i + 1
if i <= n then return i, Data[i] end
end
end
new_t.__dump = function()
-- remove the not serializable meta data
return {Type = "Array", MemSize = MemSize, Data = Data}
end
new_t.__load = function(memsize, data)
MemSize = memsize
Data = data
end
return setmetatable(new_t, mt)
end
function safer_lua.Set(...)
local new_t = {}
local mt = {}
local MemSize = 0
local Size = 0
local Data = {}
mt.__newindex = function(t, k, v) return end
mt.count = var_count
for idx = 1,select('#',...) do
local v = select(idx,...)
local cnt = mt.count(v)
if cnt then
MemSize = MemSize + cnt
Size = Size + 1
rawset(Data, v, true)
end
end
new_t.add = function(k)
MemSize = MemSize + mt.count(k)
rawset(Data, k, true)
Size = Size + 1
end
new_t.del = function(k)
MemSize = MemSize - mt.count(k)
rawset(Data, k, nil)
Size = Size - 1
end
new_t.has = function(k)
return rawget(Data, k) == true
end
new_t.memsize = function(t)
return MemSize
end
new_t.size = function(t)
return Size
end
new_t.next = function(t)
local i = 0
local n = nil
return function ()
i = i + 1
n = next(Data, n)
if n then return i, n end
end
end
new_t.__dump = function()
-- remove the not serializable meta data
return {Type = "Set", Size = Size, MemSize = MemSize, Data = Data}
end
new_t.__load = function(size, memsize, data)
Size = size
MemSize = memsize
Data = data
end
return setmetatable(new_t, mt)
end
-- remove the not serializable meta data
function safer_lua.datastruct_to_table(ds)
return ds.__dump()
end
-- add the not serializable meta data again
function safer_lua.table_to_datastruct(tbl)
if tbl.Type == "Store" then
local s = safer_lua.Store()
s.__load(tbl.Size, tbl.MemSize, tbl.Data)
return s
elseif tbl.Type == "Set" then
local s = safer_lua.Set()
s.__load(tbl.Size, tbl.MemSize, tbl.Data)
return s
elseif tbl.Type == "Array" then
local a = safer_lua.Array()
a.__load(tbl.MemSize, tbl.Data)
return a
end
end