Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 57 additions & 134 deletions tracedoc.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local next = next
local pairs = pairs
local ipairs = ipairs
local setmetatable = setmetatable
local getmetatable = getmetatable
local type = type
Expand All @@ -10,93 +11,17 @@ local tracedoc = {}
local NULL = setmetatable({} , { __tostring = function() return "NULL" end }) -- nil
tracedoc.null = NULL
local tracedoc_type = setmetatable({}, { __tostring = function() return "TRACEDOC" end })
local tracedoc_len = setmetatable({} , { __mode = "kv" })

local function doc_next(doc, key)
-- at first, iterate all the keys changed
local change_keys = doc._keys
if key == nil or change_keys[key] then
while true do
key = next(change_keys, key)
if key == nil then
break
end
local v = doc[key]
if v ~= nil then
return key, v
end
end
end

-- and then, iterate all the keys in lastversion except keys changed

local lastversion = doc._lastversion

while true do
key = next(lastversion, key)
if key == nil then
return
end
if not change_keys[key] then
local v = doc[key]
if v ~= nil then
return key, v
end
end
end
return next(doc._stage, key)
end

local function doc_pairs(doc)
return doc_next, doc
end

local function find_length_after(doc, idx)
local v = doc[idx + 1]
if v == nil then
return idx
end
repeat
idx = idx + 1
v = doc[idx + 1]
until v == nil
tracedoc_len[doc] = idx
return idx
end

local function find_length_before(doc, idx)
if idx <= 1 then
tracedoc_len[doc] = nil
return 0
end
repeat
idx = idx - 1
until idx <=0 or doc[idx] ~= nil
tracedoc_len[doc] = idx
return idx
return pairs(doc._stage)
end

local function doc_len(doc)
local len = tracedoc_len[doc]
if len == nil then
len = #doc._lastversion
tracedoc_len[doc] = len
end
if len == 0 then
return find_length_after(doc, 0)
end
local v = doc[len]
if v == nil then
return find_length_before(doc, len)
end
return find_length_after(doc, len)
end

local function doc_read(doc, k)
if doc._keys[k] then
return doc._changes[k]
end
-- if k is not changed, return lastversion
return doc._lastversion[k]
return #doc._stage
end

local function doc_change(doc, k, v)
Expand All @@ -114,17 +39,20 @@ local function doc_change(doc, k, v)
if type(v) == "table" then
local vt = getmetatable(v)
if vt == nil then
local lv = doc._lastversion[k]
local lv = doc._stage[k]
if getmetatable(lv) ~= tracedoc_type then
-- last version is not a table, new a empty one
lv = tracedoc.new()
lv._parent = doc
doc._lastversion[k] = lv
elseif doc[k] == nil then
-- this version is clear first, deepcopy lastversion one
lv = tracedoc.new(lv)
lv._parent = doc
doc._lastversion[k] = lv
lv = doc._changed_values[k]
if getmetatable(lv) ~= tracedoc_type then
-- last version is not a table, new a empty one
lv = tracedoc.new()
lv._parent = doc
doc._stage[k] = lv
else
-- this version is clear first, deepcopy lastversion one
lv = tracedoc.new(lv)
lv._parent = doc
doc._stage[k] = lv
end
end
local keys = {}
for k in pairs(lv) do
Expand All @@ -139,33 +67,33 @@ local function doc_change(doc, k, v)
for k in pairs(keys) do
lv[k] = nil
end
-- don't cache sub table into doc._changes
doc._changes[k] = nil
doc._keys[k] = nil
-- don't cache sub table into doc._changed_values
doc._changed_values[k] = nil
doc._changed_keys[k] = nil
return
end
end
doc._changes[k] = v
doc._keys[k] = true
doc._changed_values[k] = doc._stage[k]
doc._changed_keys[k] = true
doc._stage[k] = v
end

local doc_mt = {
__newindex = doc_change,
__index = doc_read,
__pairs = doc_pairs,
__len = doc_len,
__metatable = tracedoc_type, -- avoid copy by ref
}

function tracedoc.new(init)
local doc_stage = {}
local doc = {
_dirty = false,
_parent = false,
_changes = {},
_keys = {},
_lastversion = {},
_changed_keys = {},
_changed_values = {},
_stage = doc_stage,
}
setmetatable(doc, doc_mt)
setmetatable(doc, {
__newindex = doc_change,
__index = doc_stage,
__pairs = doc_pairs,
__len = doc_len,
__metatable = tracedoc_type, -- avoid copy by ref
})
if init then
for k,v in pairs(init) do
-- deepcopy v
Expand All @@ -180,47 +108,42 @@ function tracedoc.new(init)
end

function tracedoc.dump(doc)
local last = {}
for k,v in pairs(doc._lastversion) do
table.insert(last, string.format("%s:%s",k,v))
end
local changes = {}
for k,v in pairs(doc._changes) do
table.insert(changes, string.format("%s:%s",k,v))
local stage = {}
for k,v in pairs(doc._stage) do
table.insert(stage, string.format("%s:%s",k,v))
end
local keys = {}
for k in pairs(doc._keys) do
table.insert(keys, k)
local changed = {}
for k in pairs(doc._changed_keys) do
table.insert(changed, string.format("%s:%s",k,doc._changed_values[k]))
end
return string.format("last [%s]\nchanges [%s]\nkeys [%s]",table.concat(last, " "), table.concat(changes," "), table.concat(keys," "))
return string.format("content [%s]\nchanged [%s]",table.concat(stage, " "), table.concat(changed," "))
end

function tracedoc.commit(doc, result, prefix)
if doc._ignore then
return result
end
doc._dirty = false
local lastversion = doc._lastversion
local changes = doc._changes
local keys = doc._keys
local stage = doc._stage
local changed_values = doc._changed_values
local changed_keys = doc._changed_keys
local dirty = false
if next(keys) ~= nil then
for k in next, keys do
local v = changes[k]
keys[k] = nil
changes[k] = nil
if lastversion[k] ~= v then
dirty = true
if result then
local key = prefix and prefix .. k or k
result[key] = v == nil and NULL or v
result._n = (result._n or 0) + 1
end
lastversion[k] = v
for k in pairs(changed_keys) do
local v = stage[k]
local lv = changed_values[k]
changed_keys[k] = nil
changed_values[k] = nil

if lv ~= v then
dirty = true
if result then
local key = prefix and prefix .. k or k
result[key] = v == nil and NULL or v
result._n = (result._n or 0) + 1
end
end
end
for k,v in pairs(lastversion) do
for k,v in pairs(stage) do
if getmetatable(v) == tracedoc_type and v._dirty then
if result then
local key = prefix and prefix .. k or k
Expand Down