Fixed an issue with mixed tables that may be missing keys.

Earlier optimization depended on incorrect assumption that `next` can be
started by using #t, which is not the case.
This commit is contained in:
Paul Kulchenko 2013-10-01 20:52:34 -07:00
parent 29d2e11920
commit a4e258cb77
2 changed files with 52 additions and 5 deletions

View File

@ -11,7 +11,7 @@ for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'})
for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end
local function s(t, opts)
local name, indent, fatal = opts.name, opts.indent, opts.fatal
local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
@ -54,11 +54,14 @@ local function s(t, opts)
if level >= maxl then return tag..'{}'..comment('max', level) end
seen[t] = insref or spath
if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
local maxn, o, out, maxt = math.min(#t, opts.maxnum or #t), {}, {}, #t > 0 and #t or nil
local maxn, o, out = math.min(#t, maxnum or #t), {}, {}
for key = 1, maxn do o[key] = key end
for key in next, t, maxt do if not o[key] or key > maxn then o[#o+1] = key end end
if opts.sortkeys and next(t, maxt) ~= nil then alphanumsort(o, t, opts.sortkeys) end
if opts.maxnum and #o > opts.maxnum then o[opts.maxnum+1] = nil end
if not maxnum or #o < maxnum then
local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables
for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end
if maxnum and #o > maxnum then o[maxnum+1] = nil end
if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end
local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output)
for n, key in ipairs(o) do
local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing

View File

@ -235,4 +235,48 @@ do
assert(#_a.b == 2, "table with maxnum=3 has no more than 3 elements 3/3: failed")
end
-- test serialization of mixed tables
do
local a = {a='a', b='b', c='c', [3]=3, [2]=2,[1]=1}
local diffable = {sortkeys = true, comment = false, nocode = true, indent = ' '}
local _a = assert(loadstring(serpent.dump(a, diffable)))()
for k,v in pairs(a) do
assert(v == _a[k],
("mixed table with sorted keys (key = '%s'): failed"):format(k))
end
end
-- test sorting is not called on numeric-only tables
do
local a = {1,2,3,4,5}
local called = false
local sortfunc = function() called = true end
serpent.dump(a, {sortkeys = sortfunc, sparse = false})
assert(called == false, "sorting is not called on numeric-only tables: failed")
called = false
serpent.dump(a, {sortkeys = sortfunc, sparse = false, maxnum = 3})
assert(called == false, "sorting is not called on numeric-only tables with maxnum: failed")
end
-- test serializing large numeric-only tables
do
local a, str = {}
for i = 1, 100000 do a[i] = i end
local start = os.clock()
str = serpent.dump(a)
print("dump: "..(os.clock() - start), #str)
start = os.clock()
str = serpent.dump(a, {maxnum = 400})
print("dump/maxnum: "..(os.clock() - start), #str)
start = os.clock()
str = serpent.dump(a, {sparse = false})
print("dump/sparse=false: "..(os.clock() - start), #str)
end
print("All tests passed.")