mirror of https://github.com/odrling/Aegisub
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
|
@ -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,22 +39,31 @@ 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)
|
||||||
|
-- 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 = {}
|
local newtab = {}
|
||||||
for key, val in pairs(newtab) do
|
-- 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
|
if type(val) == "table" then
|
||||||
if not circular[val] then
|
newtab[key] = do_copy(val)
|
||||||
circular[val] = do_copy(val)
|
|
||||||
end
|
|
||||||
newtab[key] = circular[val]
|
|
||||||
else
|
else
|
||||||
newtab[key] = val
|
newtab[key] = val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return newtab
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return do_copy(srctab)
|
return do_copy(srctab)
|
||||||
end
|
end
|
||||||
|
|
|
@ -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 New Issue