Skip to content

Instantly share code, notes, and snippets.

@SoniEx2
Last active August 29, 2015 14:01
Show Gist options
  • Save SoniEx2/fc5d3614614e4e3fe131 to your computer and use it in GitHub Desktop.
Save SoniEx2/fc5d3614614e4e3fe131 to your computer and use it in GitHub Desktop.
IRC friendly Lua table copy
-- the following deep copy function never stack overflows from recursion
-- needs optimization tho
local function deep(inp,copies)
-- push
local stack = {{inp = inp, key = nil, value = nil, next = false, skipNext = false, out = nil}}
local currentResult
copies = (type(copies) == "table") and copies or {}
-- I run ZBS from stock Lua (5.1), it's not label-friendly
while true do
--::Return::
if #stack == 0 then
return currentResult
end
local entry = stack[#stack]
if not entry.next then
entry.out = {}
copies[entry.inp] = entry.out
end
--::Next::
local rep = false
local ret = false
while true do
repeat
local key,value = entry.key, entry.value
if not entry.skipNext then
key,value = next(entry.inp, entry.key)
if not key then
--goto Out
break
end
entry.key = key
end
entry.skipNext = false
entry.next = true
print(key, value)
if (type(key) ~= "table" or copies[key]) and (type(value) ~= "table" or copies[value]) then
rawset(entry.out,copies[key] or key,copies[value] or value)
entry.key,entry.value = key,value
--goto Next
rep = true
break
end
if not (type(key) ~= "table" or copies[key]) then
-- push
entry.skipNext = true
stack[#stack+1] = {inp = key, key = nil, value = nil, next = false, skipNext = false, out = nil}
elseif not (type(value) ~= "table" or copies[value]) then
-- push
entry.skipNext = true
stack[#stack+1] = {inp = value, key = nil, value = nil, next = false, skipNext = false, out = nil}
end
--goto Return
ret = true
break
until true
if not rep then break end
rep = false
end
if not ret then
--::Out::
currentResult = entry.out
-- pop
stack[#stack] = nil
end
--goto Return
end
end
-- alt deepcopy function
-- not fully tested
-- good for tables with long chains, (as in a.b.c.d.e.etc...) bad for tables with lots of (unique table) entries (if you need this use the recursive one)
local function check(obj, todo, copies)
if copies[obj] ~= nil then
return copies[obj]
elseif type(obj) == "table" then
local t = {}
todo[obj] = t
copies[obj] = t
return t
end
return obj
end
local function deep(inp, copies)
local out, todo = {}, {}
copies = copies or {}
todo[inp], copies[inp] = out, out
-- we can't use pairs() here because we modify todo
while next(todo) do
local i, o = next(todo)
todo[i] = nil
for k, v in next, i do
rawset(o, check(k, todo, copies), check(v, todo, copies))
end
end
return out
end
local k,o,d = next,type
d = function(a,r,t) -- this function is d(eep), we use the function args to declare locals
if o{}~=o(a) then -- if type({}) (another way to get the string "table") ~= type(a) then
return a -- we can't copy it so we just return it
end
t={}
r=r or {}
r[a]=t -- set t as the recursive copy of a. you can override this with metatables
for x,y in k,a do -- for x,y in pairs(a) do (except we skip metatables by using next directly
t[r[x] or d(x,r)]=r[y] or d(y,r) -- t[copy of x] = copy of y !!! I should've used rawset here !!!
end
return t
end
-- set table.copy.shallow and table.copy.deep
-- we could also set the metatable so that calling it calls table.copy.deep
-- (or turn it into table.copy(table,deep) where deep is a boolean)
table.copy={shallow=function(a,b) -- this function is s(hallow)
b = {} -- the b argument is to save a "local " keyword (6 chars, where ",b" is 2)
for x,y in k,a do -- for x,y in pairs(a) do (except we skip metatables by using next directly)
b[x] = y
end
return b
end,deep=d}
local k,o,d=next,type d=function(a,r,t)if o{}~=o(a)then return a end t={}r=r or{}r[a]=t for x,y in k,a do t[r[x]or d(x,r)]=r[y]or d(y,r)end return t end table.copy={shallow=function(a,b)b={}for x,y in k,a do b[x]=y end return b end,deep=d}
-- one line, fits IRC just fine
-- local copies
local next,type = next,type
local function deep(inp,copies)
if type(inp) ~= "table" then
return inp
end
local out = {}
copies = (type(copies) == "table") and copies or {}
copies[inp] = out -- use normal assignment so we use copies' metatable (if any)
for key,value in next,inp do -- skip metatables by using next directly
-- we want a copy of the key and the value
-- if one is not available on the copies table, we have to make one
-- we can't do normal assignment here because metatabled copies tables might set metatables
-- out[copies[key] or deep(key,copies)]=copies[value] or deep(value,copies)
rawset(out,copies[key] or deep(key,copies),copies[value] or deep(value,copies))
end
return out
end
local function shallow(inp)
local out = {}
for key,value in next,inp do -- skip metatables by using next directly
out[key] = value
end
return out
end
-- set table.copy.shallow and table.copy.deep
-- we could also set the metatable so that calling it calls table.copy.deep
-- (or turn it into table.copy(table,deep) where deep is a boolean)
table.copy={shallow=shallow,deep=deep}
-- ////////////
-- // ADDONS //
-- ////////////
-- START metatable deep copy
local mtdeepcopy_mt = {
__newindex = function(t,k,v)
setmetatable(v,debug.getmetatable(k))
rawset(t,k,v)
end
}
table.copy.deep_keep_metatable = function(inp)
return deep(inp,setmetatable({},mtdeepcopy_mt))
end
-- END metatable deep copy
-- START metatable shallow copy
local mtshallowcopy_mt = {
__newindex = function(t,k,v) -- don't rawset() so that __index gets called
setmetatable(v,debug.getmetatable(k))
end,
__index = function(t,k)
return k
end
}
table.copy.shallow_keep_metatable = function(inp)
return deep(inp,setmetatable({},mtshallowcopy_mt))
end
-- END metatable shallow copy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment