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.
Redistribution and use in source and binary forms, with or without
@ -39,21 +39,30 @@ end
copy_line = table.copy
-- 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)
-- Table to hold subtables already copied, to avoid circular references causing infinite recursion
local circular = {}
local function do_copy(oldtab)
local newtab = {}
for key, val in pairs(newtab) do
if type(val) == "table" then
if not circular[val] then
circular[val] = do_copy(val)
-- Check if we know the source already
if circular[oldtab] then
-- Use already-made copy
return circular[oldtab]
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
newtab[key] = circular[val]
else
newtab[key] = val
end
return newtab
end
end
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)