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:
parent
816b12cec6
commit
ce4babb192
2 changed files with 56 additions and 10 deletions
|
@ -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)
|
||||
|
|
37
aegisub/automation/tests/test-tablecopy-recursive.lua
Normal file
37
aegisub/automation/tests/test-tablecopy-recursive.lua
Normal 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)
|
Loading…
Reference in a new issue