Update table.copy_deep function to correctly handle self-referencing tables and tables with circular references. Doesn't handle tables with meta-tables overriding regular field get/set behaviour but that isn't intended either way. Also add a test of this.

Originally committed to SVN as r4645.
This commit is contained in:
Niels Martin Hansen 2010-06-30 00:36:25 +00:00
parent 816b12cec6
commit ce4babb192
2 changed files with 56 additions and 10 deletions

View file

@ -1,5 +1,5 @@
--[[ --[[
Copyright (c) 2005-2006, Niels Martin Hansen, Rodrigo Braz Monteiro Copyright (c) 2005-2010, Niels Martin Hansen, Rodrigo Braz Monteiro
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -39,21 +39,30 @@ end
copy_line = table.copy copy_line = table.copy
-- Make a deep copy of a table -- Make a deep copy of a table
-- This will do infinite recursion if there's any circular references. (Eg. if you try to copy _G) -- Retains equality of table references inside the copy and handles self-referencing structures
function table.copy_deep(srctab) function table.copy_deep(srctab)
-- Table to hold subtables already copied, to avoid circular references causing infinite recursion -- Table to hold subtables already copied, to avoid circular references causing infinite recursion
local circular = {} local circular = {}
local function do_copy(oldtab) local function do_copy(oldtab)
local newtab = {} -- Check if we know the source already
for key, val in pairs(newtab) do if circular[oldtab] then
if type(val) == "table" then -- Use already-made copy
if not circular[val] then return circular[oldtab]
circular[val] = do_copy(val) else
-- Prepare a new table to copy into
local newtab = {}
-- Register it as known
circular[oldtab] = newtab
-- Copy fields
for key, val in pairs(oldtab) do
-- Copy tables recursively, everything else normally
if type(val) == "table" then
newtab[key] = do_copy(val)
else
newtab[key] = val
end end
newtab[key] = circular[val]
else
newtab[key] = val
end end
return newtab
end end
end end
return do_copy(srctab) return do_copy(srctab)

View file

@ -0,0 +1,37 @@
script_name = "Test table.copy_deep"
script_description = "Tests the Auto4 Lua utils.lua table.copy_deep function"
script_author = "Niels Martin Hansen"
include "utils.lua"
function test_tablecopy_deep()
local function test_table(tab, desc)
local l = aegisub.log
l("--- %15s -------------\n", desc)
l("tab.a = %d\n", tab.a)
l("type(tab.b) = %s\n", type(tab.b))
l("tab.b.a = %s\n", tab.b.a)
l("tab.c==tab.b ? %d\n", (tab.c==tab.b) and 1 or 0)
l("type(tab.b.b) = %s\n", type(tab.b.b))
l("type(tab.d) = %s\n", type(tab.d))
l("tab.d.a == tab.d ? %d\n", (tab.d.a==tab.d) and 1 or 0)
l("tab.e == tab ? %d\n", (tab.e==tab) and 1 or 0)
l("\n")
end
local orgtab = {}
orgtab.a = 1
orgtab.b = {}
orgtab.b.a = "hi"
orgtab.c = orgtab.b
orgtab.c.b = test_table
orgtab.d = {}
orgtab.d.a = orgtab.d
orgtab.e = orgtab
test_table(orgtab, "Original table")
local cpytab = table.copy_deep(orgtab)
test_table(cpytab, "Copied table")
end
aegisub.register_macro("TEST table.copy_deep", "", test_tablecopy_deep)