Skip to content

Commit

Permalink
fix: deepcopy should copy same table exactly only once
Browse files Browse the repository at this point in the history
  • Loading branch information
Revolyssup committed Dec 24, 2024
1 parent 44391e0 commit b16a011
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 4 deletions.
12 changes: 8 additions & 4 deletions apisix/core/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,20 @@ end
local deepcopy
do
local function _deepcopy(orig, copied)
-- prevent infinite loop when a field refers its parent
copied[orig] = true
-- If the array-like table contains nil in the middle,
-- the len might be smaller than the expected.
-- But it doesn't affect the correctness.
local len = #orig
local copy = new_tab(len, nkeys(orig) - len)
-- prevent infinite loop when a field refers its parent
copied[orig] = copy
for orig_key, orig_value in pairs(orig) do
if type(orig_value) == "table" and not copied[orig_value] then
copy[orig_key] = _deepcopy(orig_value, copied)
if type(orig_value) == "table" then
if copied[orig_value] then
copy[orig_key] = copied[orig_value]
else
copy[orig_key] = _deepcopy(orig_value, copied)
end
else
copy[orig_key] = orig_value
end
Expand Down
112 changes: 112 additions & 0 deletions t/core/table.t
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,115 @@ GET /t
GET /t
--- response_body
ok



=== TEST 8: deepcopy copy same table only once
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local tmp = { name = "tmp", priority = 1, enabled = true }
local origin = { a = { b = tmp }, c = tmp}
local copy = core.table.deepcopy(origin)
if not core.table.deep_eq(copy, origin) then
ngx.say("copy: ", json.encode(expect), ", origin: ", json.encode(actual))
return
end
if copy.a.b ~= copy.c then
ngx.say("copy.a.b should be the same as copy.c")
return
end
ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok



=== TEST 9: reference same table
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local deepcopy = core.table.deepcopy
local tab1 = {name = "tab1"}
local tab2 = {
a = tab1,
b = tab1
}
local tab_copied = deepcopy(tab2)

ngx.say("table copied: ", require("toolkit.json").encode(tab_copied))

ngx.say("tab1 == tab2.a: ", tab1 == tab2.a)
ngx.say("tab2.a == tab2.b: ", tab2.a == tab2.b)

ngx.say("tab_copied.a == tab1: ", tab_copied.a == tab1)
ngx.say("tab_copied.a == tab_copied.b: ", tab_copied.a == tab_copied.b)
}
}
--- request
GET /t
--- response_body
table copied: {"a":{"name":"tab1"},"b":{"name":"tab1"}}
tab1 == tab2.a: true
tab2.a == tab2.b: true
tab_copied.a == tab1: false
tab_copied.a == tab_copied.b: true



=== TEST 10: reference table self(root node)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local deepcopy = core.table.deepcopy
local tab1 = {name = "tab1"}
local tab2 = {
a = tab1,
}
tab2.c = tab2

local tab_copied = deepcopy(tab2)

ngx.say("tab_copied.a == tab1: ", tab_copied.a == tab_copied.b)
ngx.say("tab_copied == tab_copied.c: ", tab_copied == tab_copied.c)
}
}
--- request
GET /t
--- response_body
tab_copied.a == tab1: false
tab_copied == tab_copied.c: true



=== TEST 11: reference table self(sub node)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local deepcopy = core.table.deepcopy
local tab_org = {
a = {
a2 = "a2"
},
}
tab_org.b = tab_org.a

local tab_copied = deepcopy(tab_org)
ngx.say("table copied: ", require("toolkit.json").encode(tab_copied))
ngx.say("tab_copied.a == tab_copied.b: ", tab_copied.a == tab_copied.b)
}
}
--- request
GET /t
--- response_body
table copied: {"a":{"a2":"a2"},"b":{"a2":"a2"}}
tab_copied.a == tab_copied.b: true

0 comments on commit b16a011

Please sign in to comment.